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'])
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))
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)
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
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.')
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.')
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()
def __init__(self, app_context): super(ComputeQuestionStats, self).__init__(app_context) self._course = courses.Course(None, app_context)
def __init__(self, app_context): super(ComputeStudentProgressStats, self).__init__(app_context) self._course = courses.Course(None, app_context)
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
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))
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
def course(self): return self._course or courses.Course(None, self.app_context)
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))