Beispiel #1
0
 def post_add_lesson(self):
     """Adds new lesson to a first unit of the course."""
     course = courses.Course(self)
     target_unit = None
     if self.request.get('unit_id'):
         target_unit = course.find_unit_by_id(self.request.get('unit_id'))
     else:
         for unit in course.get_units():
             if unit.type == verify.UNIT_TYPE_UNIT:
                 target_unit = unit
                 break
     if target_unit:
         lesson = course.add_lesson(target_unit)
         course.save()
         # TODO(psimakov): complete 'edit_lesson' view
         self.redirect(self.get_action_url(
             'edit_lesson', key=lesson.lesson_id,
             extra_args={'is_newly_created': 1}))
     else:
         self.redirect('/dashboard')
    def put(self):
        """Handles REST PUT verb with JSON payload."""
        request = transforms.loads(self.request.get('request'))

        if not self.assert_xsrf_token_or_fail(request, self.XSRF_TOKEN,
                                              {'key': None}):
            return

        if not CourseOutlineRights.can_edit(self):
            transforms.send_json_response(self, 401, 'Access denied.', {})
            return

        payload = request.get('payload')
        payload_dict = transforms.json_to_dict(transforms.loads(payload),
                                               self.SCHEMA_DICT)
        course = courses.Course(self)
        course.reorder_units(payload_dict['outline'])
        course.save()

        transforms.send_json_response(self, 200, 'Saved.')
    def put(self):
        """Handles HTTP PUT verb."""
        request = transforms.loads(self.request.get('request'))
        if not self.assert_xsrf_token_or_fail(request, 'add-course-put', {}):
            return

        if not CoursesPropertyRights.can_add():
            transforms.send_json_response(self, 401, 'Access denied.')
            return

        payload = request.get('payload')
        json_object = transforms.loads(payload)
        name = json_object.get('name')
        title = json_object.get('title')
        admin_email = json_object.get('admin_email')

        # Add the new course entry.
        errors = []
        entry = sites.add_new_course_entry(name, title, admin_email, errors)
        if not entry:
            errors.append('Error adding a new course entry.')
        if errors:
            transforms.send_json_response(self, 412, '\n'.join(errors))
            return

        # We can't expect our new configuration being immediately available due
        # to datastore queries consistency limitations. So we will instantiate
        # our new course here and not use the normal sites.get_all_courses().
        app_context = sites.get_all_courses(entry)[0]

        # Update course with a new title and admin email.
        new_course = courses.Course(None, app_context=app_context)
        if not new_course.init_new_course_settings(title, admin_email):
            transforms.send_json_response(
                self, 412,
                'Added new course entry, but failed to update title and/or '
                'admin email. The course.yaml file already exists and must be '
                'updated manually.')
            return

        transforms.send_json_response(self, 200, 'Added.', {'entry': entry})
    def put(self):
        """Handles REST PUT verb with JSON payload."""
        request = transforms.loads(self.request.get('request'))

        if not self.assert_xsrf_token_or_fail(request, 'import-course',
                                              {'key': None}):
            return

        if not CourseOutlineRights.can_edit(self):
            transforms.send_json_response(self, 401, 'Access denied.', {})
            return

        payload = request.get('payload')
        course_raw = transforms.json_to_dict(transforms.loads(payload),
                                             self.SCHEMA_DICT)['course']

        source = None
        for acourse in sites.get_all_courses():
            if acourse.raw == course_raw:
                source = acourse
                break

        if not source:
            transforms.send_json_response(self, 404, 'Object not found.',
                                          {'raw': course_raw})
            return

        course = courses.Course(self)
        errors = []
        try:
            course.import_from(source, errors)
        except Exception as e:  # pylint: disable=broad-except
            logging.exception(e)
            errors.append('Import failed: %s' % e)

        if errors:
            transforms.send_json_response(self, 412, '\n'.join(errors))
            return

        course.save()
        transforms.send_json_response(self, 200, 'Imported.')
    def test_cron(self):
        app_context = sites.get_all_courses()[0]
        app_context.set_current_locale('en_US')
        course = courses.Course(None, app_context=app_context)
        actions.login('*****@*****.**', is_admin=True)

        # Call cron URL without indexing enabled; expect 0 results found.
        self.get(search.CronIndexCourse.URL)
        self.execute_all_deferred_tasks()
        response = search.fetch(course, 'color')
        self.assertEquals(0, response['total_found'])

        # Call cron URL with indexing enabled; expect results.
        with actions.OverriddenEnvironment(
            {'course': {
                search.AUTO_INDEX_SETTING: 'True'
            }}):
            self.get(search.CronIndexCourse.URL)
        self.execute_all_deferred_tasks()
        response = search.fetch(course, 'color')
        self.assertEquals(1, response['total_found'])
Beispiel #6
0
    def get(self):
        """Handles GET REST verb and returns lesson object as JSON payload."""

        if not CourseOutlineRights.can_view(self):
            transforms.send_json_response(self, 401, 'Access denied.', {})
            return

        key = self.request.get('key')
        course = courses.Course(self)
        lesson = course.find_lesson_by_id(None, key)
        assert lesson
        payload_dict = self.get_lesson_dict(course, lesson)

        message = ['Success.']
        if self.request.get('is_newly_created'):
            message.append('New lesson has been created and saved.')

        transforms.send_json_response(
            self, 200, '\n'.join(message),
            payload_dict=payload_dict,
            xsrf_token=XsrfTokenManager.create_xsrf_token('lesson-edit'))
    def setUp(self):
        super(CodeTagTests, self).setUp()

        actions.login(self.ADMIN_EMAIL, is_admin=True)
        self.base = '/' + self.COURSE_NAME

        app_context = actions.simple_add_course(self.COURSE_NAME,
                                                self.ADMIN_EMAIL,
                                                'Test Course')
        self.course = courses.Course(None, app_context=app_context)

        self.code_tag_unit = self.course.add_unit()
        self.code_tag_unit.title = 'code_tag_test_unit'
        self.code_tag_unit.unit_header = self.CODE_TAG_TEMPLATE % (
            self.CODE_TYPE, self.CODE_EXAMPLE)

        self.no_code_tag_unit = self.course.add_unit()
        self.no_code_tag_unit.title = 'no_code_tag_test_unit'
        self.no_code_tag_unit.unit_header = 'Unit without code example tags.'

        self.course.save()
    def test_youtube(self):
        sites.setup_courses('course:/test::ns_test, course:/:/')
        default_namespace = namespace_manager.get_namespace()
        try:
            namespace_manager.set_namespace('ns_test')

            course = courses.Course(None,
                                    app_context=sites.get_all_courses()[0])
            unit = course.add_unit()
            unit.availability = courses.AVAILABILITY_AVAILABLE
            lesson_a = course.add_lesson(unit)
            lesson_a.video = 'portal'
            lesson_a.availability = courses.AVAILABILITY_AVAILABLE
            lesson_b = course.add_lesson(unit)
            lesson_b.objectives = '<gcb-youtube videoid="glados">'
            lesson_b.availability = courses.AVAILABILITY_AVAILABLE
            course.update_unit(unit)
            course.save()

            entity = announcements.AnnouncementEntity.make(
                'New Announcement', '<gcb-youtube videoid="aperature">', False)
            entity.put()

            self.index_test_course()

            response = self.get('/test/search?query=apple')
            self.assertIn('gcb-search-result', response.body)
            self.assertIn('start=3.14', response.body)
            self.assertIn('v=portal', response.body)
            self.assertIn('v=glados', response.body)
            self.assertIn('v=aperature', response.body)
            self.assertIn('lemon', response.body)
            self.assertIn('Medicus Quis', response.body)
            self.assertIn('http://thumbnail.null', response.body)

            # Test to make sure empty notes field doesn't cause a urlfetch
            response = self.get('/test/search?query=cogito')
            self.assertNotIn('gcb-search-result', response.body)
        finally:
            namespace_manager.set_namespace(default_namespace)
    def get_in_place_lesson_editor(cls, handler):
        """Shows the lesson editor iframed inside a lesson page."""
        if not handler.app_context.is_editable_fs():
            return

        key = handler.request.get('key')

        course = courses.Course(handler)
        lesson = course.find_lesson_by_id(None, key)
        annotations_dict = (None if lesson.has_activity else
                            cls.HIDE_ACTIVITY_ANNOTATIONS)
        schema = LessonRESTHandler.get_schema(course, key)
        annotations_dict = schema.get_schema_dict() + annotations_dict

        if courses.has_only_new_style_activities(course):
            schema.get_property('objectives').extra_schema_dict_values[
                'excludedCustomTags'] = set(['gcb-activity'])

        extra_js_files = [
            'lesson_editor.js', 'in_place_lesson_editor_iframe.js'
        ] + LessonRESTHandler.EXTRA_JS_FILES

        form_html = oeditor.ObjectEditor.get_html_for(
            handler,
            schema.get_json_schema(),
            annotations_dict,
            key,
            handler.canonicalize_url(LessonRESTHandler.URI),
            None,
            additional_dirs=LessonRESTHandler.ADDITIONAL_DIRS,
            display_types=schema.get_display_types(),
            extra_css_files=LessonRESTHandler.EXTRA_CSS_FILES,
            extra_js_files=extra_js_files)
        template = handler.get_template('in_place_lesson_editor.html', [])
        template_values = {
            'form_html': form_html,
            'extra_css_href_list': handler.EXTRA_CSS_HREF_LIST,
            'extra_js_href_list': handler.EXTRA_JS_HREF_LIST
        }
        handler.response.write(template.render(template_values))
Beispiel #10
0
    def delete(self):
        """Handles REST DELETE verb with JSON payload."""
        key = self.request.get('key')

        if not self.assert_xsrf_token_or_fail(
                self.request, 'delete-unit', {'key': key}):
            return

        if not CourseOutlineRights.can_delete(self):
            transforms.send_json_response(
                self, 401, 'Access denied.', {'key': key})
            return

        course = courses.Course(self)
        unit = course.find_unit_by_id(key)
        if not unit:
            transforms.send_json_response(
                self, 404, 'Object not found.', {'key': key})
            return
        course.delete_unit(unit)
        course.save()
        transforms.send_json_response(self, 200, 'Deleted.')
    def setUp(self):
        super(LessonTests, self).setUp()
        self.base = '/' + COURSE_NAME
        app_context = actions.simple_add_course(COURSE_NAME, ADMIN_EMAIL,
                                                COURSE_NAME)
        self.course = courses.Course(None, app_context)
        self.unit = self.course.add_unit()
        self.unit.title = 'Test Unit'
        self.lesson = self.course.add_lesson(self.unit)
        self.lesson.title = 'Test Lesson'
        self.lesson.objectives = 'Lesson body'
        self.course.save()

        self.course_id = get_course_id(self.base)
        self.unit_id = get_unit_id(self.base, self.unit.unit_id)
        self.lesson_id = get_lesson_id(self.base, self.unit.unit_id,
                                       self.lesson.lesson_id)

        self.set_course_availability(
            courses.COURSE_AVAILABILITY_REGISTRATION_REQUIRED)

        actions.login(ADMIN_EMAIL)
Beispiel #12
0
    def test_get_entity_label_wrapper_in_progress_works(self):
        """Tests get_entity_label wrappers in progress.ProgressStats."""
        sites.setup_courses('course:/test::ns_test, course:/:/')
        course = courses.Course(None, app_context=sites.get_all_courses()[0])
        progress_stats = ProgressStats(course)
        unit1 = course.add_unit()

        # pylint: disable-msg=protected-access
        assert_equals(
            progress_stats._get_unit_label(unit1.unit_id),
            'Unit %s' % unit1.index)
        assessment1 = course.add_assessment()
        assert_equals(
            progress_stats._get_assessment_label(assessment1.unit_id),
            assessment1.title)
        lesson11 = course.add_lesson(unit1)
        lesson12 = course.add_lesson(unit1)
        assert_equals(
            progress_stats._get_lesson_label(unit1.unit_id, lesson11.lesson_id),
            lesson11.index)
        lesson11.has_activity = True
        course.set_activity_content(lesson11, u'var activity=[]', [])
        assert_equals(
            progress_stats._get_activity_label(
                unit1.unit_id, lesson11.lesson_id, 0), 'L1.1')
        assert_equals(
            progress_stats._get_activity_label(
                unit1.unit_id, lesson12.lesson_id, 0), 'L1.2')
        lesson12.objectives = """
            <question quid="123" weight="1" instanceid=1></question>
            random_text
            <gcb-youtube videoid="Kdg2drcUjYI" instanceid="VD"></gcb-youtube>
            more_random_text
            <question-group qgid="456" instanceid=2></question-group>
            yet_more_random_text
        """
        assert_equals(
            progress_stats._get_component_ids(
                unit1.unit_id, lesson12.lesson_id, 0), [u'1', u'2'])
    def test_course_larger_than_datastore_max_can_be_exported_and_loaded(self):
        unit = self.course.add_unit()
        num_lessons = vfs._MAX_VFS_SHARD_SIZE / len(LOREM_IPSUM)
        for unused in range(num_lessons):
            lesson = self.course.add_lesson(unit)
            lesson.objectives = LOREM_IPSUM
        self.course.save()

        other_course_name = 'other_course'
        other_course_context = actions.simple_add_course(
            other_course_name, self.ADMIN_EMAIL, 'Other')

        # Verify that a large course can be ETL'd out and recovered.
        fp, archive_file = tempfile.mkstemp(suffix='.zip')
        os.close(fp)
        try:
            parser = etl.create_args_parser()
            etl.main(
                parser.parse_args([
                    'download', 'course', '/' + self.COURSE_NAME, 'localhost',
                    '--archive_path', archive_file, '--force_overwrite',
                    '--internal', '--disable_remote'
                ]))
            etl.main(
                parser.parse_args([
                    'upload', 'course', '/' + other_course_name, 'localhost',
                    '--archive_path', archive_file, '--force_overwrite',
                    '--internal', '--disable_remote'
                ]))
        finally:
            os.unlink(archive_file)

        # Verify contents of course.
        course = courses.Course(handler=None, app_context=other_course_context)
        lessons = course.get_lessons(unit.unit_id)
        self.assertEquals(num_lessons, len(lessons))
        for lesson in lessons:
            self.assertEquals(lesson.objectives, LOREM_IPSUM)
    def test_course_larger_than_datastore_max_size_is_sharded(self):
        unit = self.course.add_unit()
        num_lessons = vfs._MAX_VFS_SHARD_SIZE / len(LOREM_IPSUM)
        for unused in range(num_lessons):
            lesson = self.course.add_lesson(unit)
            lesson.objectives = LOREM_IPSUM
        serializer = courses.PersistentCourse13(
            next_id=self.course._model.next_id,
            units=self.course._model.units,
            lessons=self.course._model.lessons)
        serialized_length = len(serializer.serialize())
        self.assertGreater(
            serialized_length, vfs._MAX_VFS_SHARD_SIZE,
            'Verify that serialized course is larger than the max entity size')

        self.course.save()
        # course.save() clears cache, so we don't need to do that here.

        # Verify contents of course.
        course = courses.Course(handler=None, app_context=self.app_context)
        lessons = course.get_lessons(unit.unit_id)
        self.assertEquals(num_lessons, len(lessons))
        for lesson in lessons:
            self.assertEquals(lesson.objectives, LOREM_IPSUM)

        # Verify that sharded items exist with appropriate sizes.
        file_key_names = vfs.DatastoreBackedFileSystem._generate_file_key_names(
            '/data/course.json', serialized_length)
        self.assertEquals(
            2, len(file_key_names),
            'Verify attempting to store a too-large file makes multiple shards'
        )
        with common_utils.Namespace(self.NAMESPACE):
            shard_0 = vfs.FileDataEntity.get_by_key_name(file_key_names[0])
            self.assertEquals(vfs._MAX_VFS_SHARD_SIZE, len(shard_0.data))

            shard_1 = vfs.FileDataEntity.get_by_key_name(file_key_names[1])
            self.assertGreater(len(shard_1.data), 0)
    def get_schema(self, handler):
        """The schema of the tag editor."""
        activity_list = []
        if handler:
            course = courses.Course(handler)

            if course.version == courses.COURSE_MODEL_VERSION_1_2:
                return self.unavailable_schema(
                    'Not available in file-based courses.')

            lesson_id = None
            if handler.request:
                lesson_id = handler.request.get('lesson_id')

            activity_list = []
            for unit in course.get_units():
                for lesson in course.get_lessons(unit.unit_id):
                    filename = 'activity-%s.js' % lesson.lesson_id
                    if lesson.has_activity:
                        if lesson.activity_title:
                            title = lesson.activity_title
                        else:
                            title = filename
                        name = '%s - %s (%s) ' % (unit.title, lesson.title,
                                                  title)
                        activity_list.append((filename, name))
                    elif str(lesson.lesson_id) == lesson_id:
                        name = 'Current Lesson (%s)' % filename
                        activity_list.append((filename, name))

        reg = schema_fields.FieldRegistry('Activity')
        reg.add_property(
            schema_fields.SchemaField('activityid',
                                      'Activity Id',
                                      'string',
                                      optional=True,
                                      select_data=activity_list))
        return reg
Beispiel #16
0
    def setUp(self):
        super(UnitOnOnePageTest, self).setUp()

        app_context = actions.simple_add_course(COURSE_NAME, ADMIN_EMAIL,
                                                COURSE_TITLE)
        self.course = courses.Course(None, app_context=app_context)

        self.unit = self.course.add_unit()
        self.unit.title = 'One Big Unit'
        self.unit.availability = courses.AVAILABILITY_AVAILABLE
        self.unit.show_contents_on_one_page = True

        self.top_assessment = self.course.add_assessment()
        self.top_assessment.title = 'Top Assessment'
        self.top_assessment.html_content = 'content of top assessment'
        self.top_assessment.availability = courses.AVAILABILITY_AVAILABLE
        self.unit.pre_assessment = self.top_assessment.unit_id

        self.bottom_assessment = self.course.add_assessment()
        self.bottom_assessment.title = 'Bottom Assessment'
        self.bottom_assessment.html_content = 'content of bottom assessment'
        self.bottom_assessment.availability = courses.AVAILABILITY_AVAILABLE
        self.unit.post_assessment = self.bottom_assessment.unit_id

        self.lesson_one = self.course.add_lesson(self.unit)
        self.lesson_one.title = 'Lesson One'
        self.lesson_one.objectives = 'body of lesson one'
        self.lesson_one.availability = courses.AVAILABILITY_AVAILABLE

        self.lesson_two = self.course.add_lesson(self.unit)
        self.lesson_two.title = 'Lesson Two'
        self.lesson_two.objectives = 'body of lesson two'
        self.lesson_two.availability = courses.AVAILABILITY_AVAILABLE

        self.course.save()

        actions.login(STUDENT_EMAIL)
        actions.register(self, STUDENT_EMAIL, COURSE_NAME)
    def put(self):
        """Handles REST PUT verb with JSON payload."""
        request = transforms.loads(self.request.get('request'))

        if not self.assert_xsrf_token_or_fail(request, self.XSRF_TOKEN,
                                              {'key': None}):
            return

        if not roles.Roles.is_user_allowed(
                self.app_context, custom_module,
                constants.COURSE_OUTLINE_REORDER_PERMISSION):

            transforms.send_json_response(self, 401, 'Access denied.', {})
            return

        payload = request.get('payload')
        payload_dict = transforms.json_to_dict(transforms.loads(payload),
                                               self.SCHEMA_DICT)
        course = courses.Course(self)
        course.reorder_units(payload_dict['outline'])
        course.save()

        transforms.send_json_response(self, 200, 'Saved.')
Beispiel #18
0
def submit_assessment(browser, unit_id, args, presubmit_checks=True):
    """Submits an assessment."""
    course = None

    for app_context in sites.get_all_courses():
        if app_context.get_slug() == browser.base:
            course = courses.Course(None, app_context=app_context)
            break

    assert course is not None, 'browser.base must match a course'

    if course.version == courses.COURSE_MODEL_VERSION_1_3:
        parent = course.get_parent_unit(unit_id)
        if parent is not None:
            response = browser.get(
                'unit?unit=%s&assessment=%s' % (parent.unit_id, unit_id))
        else:
            response = browser.get('assessment?name=%s' % unit_id)

    elif course.version == courses.COURSE_MODEL_VERSION_1_2:
        response = browser.get('assessment?name=%s' % unit_id)
        if presubmit_checks:
            assert_contains(
                '<script src="assets/js/assessment-%s.js"></script>' % unit_id,
                response.body)
            js_response = browser.get('assets/js/assessment-%s.js' % unit_id)
            assert_equals(js_response.status_int, 200)

    # Extract XSRF token from the page.
    match = re.search(r'assessmentXsrfToken = [\']([^\']+)', response.body)
    assert match
    xsrf_token = match.group(1)
    args['xsrf_token'] = xsrf_token

    response = browser.post('answer', args)
    assert_equals(response.status_int, 200)
    return response
    def test_get_entity_id_wrapper_in_progress_works(self):
        """Tests get_entity_id wrappers in progress.ProgressStats."""
        sites.setup_courses('course:/test::ns_test, course:/:/')
        course = courses.Course(None, app_context=sites.get_all_courses()[0])
        progress_stats = ProgressStats(course)
        unit1 = course.add_unit()

        assert_equals(progress_stats._get_unit_ids_of_type_unit(),
                      [unit1.unit_id])
        assessment1 = course.add_assessment()
        assert_equals(progress_stats._get_assessment_ids(),
                      [assessment1.unit_id])
        lesson11 = course.add_lesson(unit1)
        lesson12 = course.add_lesson(unit1)
        assert_equals(progress_stats._get_lesson_ids(unit1.unit_id),
                      [lesson11.lesson_id, lesson12.lesson_id])
        lesson11.has_activity = True
        course.set_activity_content(lesson11, u'var activity=[]', [])
        assert_equals(
            progress_stats._get_activity_ids(unit1.unit_id,
                                             lesson11.lesson_id), [0])
        assert_equals(
            progress_stats._get_activity_ids(unit1.unit_id,
                                             lesson12.lesson_id), [])
    def test_square_brackets(self):
        app_context = self._import_course()
        course = courses.Course(None, app_context=app_context)
        assessment = course.add_assessment()
        assessment.html_content = '<b>[in [square] brackets]</b>'
        course.save()
        self.run_translate_job()
        self.run_download_job('--job_args=%s --encoded_angle_brackets' %
                              self.zipfile_name)

        # Open the downloaded .zip file; check the contents of the .po file
        # to ensure we have an HTML tag converted to square brackets, and
        # that the literal square brackets in the text were escaped.
        with zipfile.ZipFile(self.zipfile_name) as zf:
            data = zf.read('locale/ln/LC_MESSAGES/messages.po')
        lines = data.split('\n')
        index = lines.index(
            'msgid "[b#1]\\\\[in \\\\[square\\\\] brackets\\\\][/b#1]"')
        self.assertGreater(index, -1)

        # Overwrite the .zip file with a new .po file that contains a
        # translated version of the text w/ square brackets, and upload.
        lines[index + 1] = (
            'msgstr "[b#1]\\\\[IN \\\\[ROUND\\\\] BRACKETS\\\\][/b#1]"')
        data = '\n'.join(lines)
        with zipfile.ZipFile(self.zipfile_name, 'w') as zf:
            zf.writestr('locale/ln/LC_MESSAGES/messages.po', data)
            zf.close()
        self.run_upload_job('--job_args=%s --encoded_angle_brackets' %
                            self.zipfile_name)

        # Verify that the translated version is visible in the page.
        actions.login('*****@*****.**', is_admin=True)
        response = self.get('first/assessment?name=%s&hl=ln' %
                            assessment.unit_id)
        self.assertIn('<b>[IN [ROUND] BRACKETS]</b>', response.body)
    def setUp(self):
        super(BaseQuestionnaireTests, self).setUp()

        actions.login(ADMIN_EMAIL, is_admin=True)
        self.base = '/' + COURSE_NAME

        test_course = actions.simple_add_course(COURSE_NAME, ADMIN_EMAIL,
                                                'Questionnaire Test Course')

        self.old_namespace = namespace_manager.get_namespace()
        namespace_manager.set_namespace('ns_%s' % COURSE_NAME)

        self.course = courses.Course(None, test_course)
        test_unit = self.course.add_unit()
        test_unit.now_available = True
        test_lesson = self.course.add_lesson(test_unit)
        test_lesson.now_available = True
        test_lesson.title = 'This is a lesson that contains a form.'
        test_lesson.objectives = '%s\n%s' % (TEST_FORM_HTML, QUESTIONNAIRE_TAG)
        self.unit_id = test_unit.unit_id
        self.lesson_id = test_lesson.lesson_id
        self.course.save()

        actions.logout()
    def delete(self):
        """Handles REST DELETE verb with JSON payload."""
        key = self.request.get('key')

        if not self.assert_xsrf_token_or_fail(self.request, 'delete-lesson',
                                              {'key': key}):
            return

        if not roles.Roles.is_course_admin(self.app_context):
            transforms.send_json_response(self, 401, 'Access denied.',
                                          {'key': key})
            return

        course = courses.Course(self)
        lesson = course.find_lesson_by_id(None, key)
        if not lesson:
            transforms.send_json_response(self, 404, 'Object not found.',
                                          {'key': key})
            return

        assert course.delete_lesson(lesson)
        course.save()

        transforms.send_json_response(self, 200, 'Deleted.')
Beispiel #23
0
    def setUp(self):
        super(AccessDraftsTestCase, self).setUp()
        actions.login(self.ADMIN_EMAIL, is_admin=True)
        self.base = '/' + self.COURSE_NAME
        self.context = actions.simple_add_course(self.COURSE_NAME,
                                                 self.ADMIN_EMAIL,
                                                 'Access Draft Testing')

        self.course = courses.Course(None, self.context)

        self.old_namespace = namespace_manager.get_namespace()
        namespace_manager.set_namespace('ns_%s' % self.COURSE_NAME)

        role_dto = models.RoleDTO(
            None, {
                'name': self.ROLE,
                'users': [self.USER_EMAIL],
                'permissions': {
                    courses_module.custom_module.name:
                    [courses_module.SEE_DRAFTS_PERMISSION]
                }
            })
        models.RoleDAO.save(role_dto)
        actions.logout()
Beispiel #24
0
 def __init__(self, app_context):
     super(ComputeQuestionStats, self).__init__(app_context)
     self._course = courses.Course(None, app_context)
Beispiel #25
0
 def __init__(self, app_context):
     super(ComputeStudentProgressStats, self).__init__(app_context)
     self._course = courses.Course(None, app_context)
Beispiel #26
0
    def _get_sample_v15_course(self):
        """Creates a course with different types of questions and returns it."""
        sites.setup_courses('course:/test::ns_test, course:/:/')
        course = courses.Course(None, app_context=sites.get_all_courses()[0])
        unit1 = course.add_unit()
        lesson1 = course.add_lesson(unit1)
        assessment_old = course.add_assessment()
        assessment_old.title = 'Old assessment'
        assessment_new = course.add_assessment()
        assessment_new.title = 'New assessment'
        assessment_peer = course.add_assessment()
        assessment_peer.title = 'Peer review assessment'

        # Create a multiple choice question.
        mcq_new_id = 1
        mcq_new_dict = {
            'description': 'mcq_new',
            'type': 0,  # Multiple choice question.
            'choices': [{
                'text': 'answer',
                'score': 1.0
            }],
            'version': '1.5'
        }
        mcq_new_dto = models.QuestionDTO(mcq_new_id, mcq_new_dict)

        # Create a short answer question.
        frq_new_id = 2
        frq_new_dict = {
            'defaultFeedback': '',
            'rows': 1,
            'description': 'short answer',
            'hint': '',
            'graders': [{
                'matcher': 'case_insensitive',
                'score': '1.0',
                'response': 'hi',
                'feedback': ''
            }],
            'question': 'short answer question',
            'version': '1.5',
            'type': 1,  # Short answer question.
            'columns': 100
        }
        frq_new_dto = models.QuestionDTO(frq_new_id, frq_new_dict)

        # Save these questions to datastore.
        models.QuestionDAO.save_all([mcq_new_dto, frq_new_dto])

        # Create a question group.
        question_group_id = 3
        question_group_dict = {
            'description': 'question_group',
            'items': [
                {'question': str(mcq_new_id)},
                {'question': str(frq_new_id)},
                {'question': str(mcq_new_id)}
            ],
            'version': '1.5',
            'introduction': ''
        }
        question_group_dto = models.QuestionGroupDTO(
            question_group_id, question_group_dict)

        # Save the question group to datastore.
        models.QuestionGroupDAO.save_all([question_group_dto])

        # Add a MC question and a question group to leesson1.
        lesson1.objectives = """
            <question quid="1" weight="1" instanceid="QN"></question>
            random_text
            <gcb-youtube videoid="Kdg2drcUjYI" instanceid="VD"></gcb-youtube>
            more_random_text
            <question-group qgid="3" instanceid="QG"></question-group>
        """

        # Add a MC question, a short answer question, and a question group to
        # new style assessment.
        assessment_new.html_content = """
            <question quid="1" weight="1" instanceid="QN2"></question>
            <question quid="2" weight="1" instanceid="FRQ2"></question>
            random_text
            <gcb-youtube videoid="Kdg2drcUjYI" instanceid="VD"></gcb-youtube>
            more_random_text
            <question-group qgid="3" instanceid="QG2"></question-group>
        """

        return course
Beispiel #27
0
    def get_markup(self, job):
        """Returns Jinja markup for student progress analytics."""

        errors = []
        stats_calculated = False
        update_message = safe_dom.Text('')

        course = courses.Course(self)
        entity_codes = (
            progress.UnitLessonCompletionTracker.EVENT_CODE_MAPPING.values())
        value = None
        course_content = None

        if not job:
            update_message = safe_dom.Text(
                'Student progress statistics have not been calculated yet.')
        else:
            if job.status_code == jobs.STATUS_CODE_COMPLETED:
                value = transforms.loads(job.output)
                stats_calculated = True
                try:
                    course_content = progress.ProgressStats(
                        course).compute_entity_dict('course', [])
                    update_message = safe_dom.Text(
                        """
                        Student progress statistics were last updated at
                        %s in about %s second(s).""" %
                        (job.updated_on.strftime(HUMAN_READABLE_TIME_FORMAT),
                         job.execution_time_sec))
                except IOError:
                    update_message = safe_dom.Text("""
                        This feature is supported by CB 1.3 and up.""")
            elif job.status_code == jobs.STATUS_CODE_FAILED:
                update_message = safe_dom.NodeList().append(
                    safe_dom.Text("""
                        There was an error updating student progress statistics.
                        Here is the message:""")).append(
                        safe_dom.Element('br')).append(
                            safe_dom.Element('blockquote').add_child(
                                safe_dom.Element('pre').add_text('\n%s' %
                                                                 job.output)))
            else:
                update_message = safe_dom.Text(
                    """
                    Student progress statistics update started at %s and is
                    running now. Please come back shortly.""" %
                    (job.updated_on.strftime(HUMAN_READABLE_TIME_FORMAT)))
        if value:
            value = transforms.dumps(value)
        else:
            value = None
        return jinja2.utils.Markup(
            self.get_template(
                'progress_stats.html', [os.path.dirname(__file__)]).render(
                    {
                        'errors': errors,
                        'progress': value,
                        'content': transforms.dumps(course_content),
                        'entity_codes': transforms.dumps(entity_codes),
                        'stats_calculated': stats_calculated,
                        'update_message': update_message,
                    },
                    autoescape=True))
Beispiel #28
0
 def any_clusterable_objects_exist(app_context):
     course = courses.Course(None, app_context=app_context)
     if course.get_units() or models.QuestionDAO.get_all():
         return True
     return False
Beispiel #29
0
 def course(self):
     return self._course or courses.Course(None, self.app_context)
Beispiel #30
0
    def get_markup(self, job):
        """Returns Jinja markup for peer review analytics."""

        errors = []
        stats_calculated = False
        update_message = safe_dom.Text('')

        course = courses.Course(self)
        serialized_units = []

        if not job:
            update_message = safe_dom.Text(
                'Peer review statistics have not been calculated yet.')
        else:
            if job.status_code == jobs.STATUS_CODE_COMPLETED:
                stats = transforms.loads(job.output)
                stats_calculated = True

                for unit in course.get_peer_reviewed_units():
                    if unit.unit_id in stats['counts_by_completed_reviews']:
                        unit_stats = (
                            stats['counts_by_completed_reviews'][unit.unit_id])
                        serialized_units.append({
                            'stats': unit_stats,
                            'title': unit.title,
                            'unit_id': unit.unit_id,
                        })
                update_message = safe_dom.Text(
                    """
                    Peer review statistics were last updated at
                    %s in about %s second(s).""" %
                    (job.updated_on.strftime(HUMAN_READABLE_TIME_FORMAT),
                     job.execution_time_sec))
            elif job.status_code == jobs.STATUS_CODE_FAILED:
                update_message = safe_dom.NodeList().append(
                    safe_dom.Text("""
                        There was an error updating peer review statistics.
                        Here is the message:""")).append(
                        safe_dom.Element('br')).append(
                            safe_dom.Element('blockquote').add_child(
                                safe_dom.Element('pre').add_text('\n%s' %
                                                                 job.output)))
            else:
                update_message = safe_dom.Text(
                    """
                    Peer review statistics update started at %s and is running
                    now. Please come back shortly.""" %
                    job.updated_on.strftime(HUMAN_READABLE_TIME_FORMAT))

        return jinja2.utils.Markup(
            self.get_template(
                'stats.html', [os.path.dirname(__file__)]).render(
                    {
                        'errors': errors,
                        'serialized_units': serialized_units,
                        'serialized_units_json':
                        transforms.dumps(serialized_units),
                        'stats_calculated': stats_calculated,
                        'update_message': update_message,
                    },
                    autoescape=True))