def test_malformed_requests(self): response = self.put(ADMIN_SETTINGS_URL, {}) payload = transforms.loads(response.body) self.assertEquals(400, payload['status']) self.assertEquals('Missing "request" parameter.', payload['message']) response = self.put(ADMIN_SETTINGS_URL, {'request': 'asdfasdf'}) payload = transforms.loads(response.body) self.assertEquals(400, payload['status']) self.assertEquals('Malformed "request" parameter.', payload['message']) response = self.put( ADMIN_SETTINGS_URL, { 'request': transforms.dumps({'xsrf_token': cgi.escape(self.xsrf_token)}) }) payload = transforms.loads(response.body) self.assertEquals(400, payload['status']) self.assertEquals('Request missing "key" parameter.', payload['message']) response = self.put( ADMIN_SETTINGS_URL, { 'request': transforms.dumps({ 'xsrf_token': cgi.escape(self.xsrf_token), 'key': 'base:after_body_tag_begins' }) }) payload = transforms.loads(response.body) self.assertEquals(400, payload['status']) self.assertEquals('Request missing "payload" parameter.', payload['message']) response = self.put( ADMIN_SETTINGS_URL, { 'request': transforms.dumps({ 'xsrf_token': cgi.escape(self.xsrf_token), 'key': 'base:after_body_tag_begins', 'payload': 'asdfsdfasdf' }) }) payload = transforms.loads(response.body) self.assertEquals(400, payload['status']) self.assertEquals('Malformed "payload" parameter.', payload['message']) response = self.put( ADMIN_SETTINGS_URL, { 'request': transforms.dumps({ 'xsrf_token': cgi.escape(self.xsrf_token), 'key': 'base:after_body_tag_begins', 'payload': '{}' }) }) payload = transforms.loads(response.body) self.assertEquals(400, payload['status']) self.assertEquals('Payload missing "hook_content" parameter.', payload['message'])
def test_update_wont_clobber_status(self): # This fixture should already have a sync time with utils.Namespace(self.app_context.namespace): dto = drive_models.DriveSyncDAO.load('6') self.assertIsNotNone(dto.last_synced) # update existing record self.assertRestStatus(self.put('rest/modules/drive/item', { 'request': transforms.dumps({ 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token( 'drive-item-rest'), 'key': '6', 'payload': transforms.dumps({ 'sync_interval': 'hour', 'version': '1.0', 'availability': 'public', }), }), }), 200) # The sync time should still exist with utils.Namespace(self.app_context.namespace): dto = drive_models.DriveSyncDAO.load('6') self.assertIsNotNone(dto.last_synced)
def _attach_filter_data(handler, element): course = courses.Course(handler) unit_list = [] assessment_list = [] for unit in course.get_units(): if verify.UNIT_TYPE_UNIT == unit.type: unit_list.append((unit.unit_id, unit.title)) if unit.is_assessment(): assessment_list.append((unit.unit_id, unit.title)) lessons_map = {} for (unit_id, unused_title) in unit_list: lessons_map[unit_id] = [ (l.lesson_id, l.title) for l in course.get_lessons(unit_id)] element.add_attribute( data_units=transforms.dumps(unit_list + assessment_list), data_lessons_map=transforms.dumps(lessons_map), data_questions=transforms.dumps( [(question.id, question.description) for question in sorted( models.QuestionDAO.get_all(), key=lambda q: q.description)] ), data_groups=transforms.dumps( [(group.id, group.description) for group in sorted( models.QuestionGroupDAO.get_all(), key=lambda g: g.description)] ), data_types=transforms.dumps([ (models.QuestionDTO.MULTIPLE_CHOICE, 'Multiple Choice'), (models.QuestionDTO.SHORT_ANSWER, 'Short Answer')]) )
def validate(self, value): value = super(_SerializedProperty, self).validate(value) try: transforms.dumps(value) except TypeError, e: raise db.BadValueError( '%s is not JSON-serializable; error was "%s"' % (value, e))
def post_data(self, key=None, rating_int=None, additional_comments=None, xsrf_token=None): if key is None: key = self.key if xsrf_token is None: xsrf_token = utils.XsrfTokenManager.create_xsrf_token('rating') request = { 'xsrf_token': xsrf_token, 'payload': transforms.dumps({ 'key': key, 'rating': rating_int, 'additional_comments': additional_comments }) } return transforms.loads( self.post('rest/modules/rating?%s', { 'request': transforms.dumps(request) }).body)
def test_adding_empty_question_group(self): QG_URL = '/%s%s' % (self.COURSE_NAME, question_group_editor.QuestionGroupRESTHandler.URI) xsrf_token = crypto.XsrfTokenManager.create_xsrf_token( question_group_editor.QuestionGroupRESTHandler.XSRF_TOKEN) description = 'Question Group' version = ( question_group_editor.QuestionGroupRESTHandler.SCHEMA_VERSIONS[0]) payload = { 'description': description, 'version': version, 'introduction': '', 'items': [] } response = self.put( QG_URL, { 'request': transforms.dumps({ 'xsrf_token': cgi.escape(xsrf_token), 'payload': transforms.dumps(payload) }) }) self.assertEquals(response.status_int, 200) payload = transforms.loads(response.body) self.assertEquals(payload['status'], 200) self.assertEquals(payload['message'], 'Saved.') question_groups_table = self._soup_table() self.assertEquals( question_groups_table.select('.description-cell a') [0].text.strip(), description)
def test_adding_empty_question_group(self): QG_URL = '/%s%s' % (self.COURSE_NAME, QuestionGroupRESTHandler.URI) xsrf_token = crypto.XsrfTokenManager.create_xsrf_token( QuestionGroupRESTHandler.XSRF_TOKEN) description = 'Question Group' payload = { 'description': description, 'version': QuestionGroupRESTHandler.SCHEMA_VERSIONS[0], 'introduction': '', 'items': [] } response = self.put( QG_URL, { 'request': transforms.dumps({ 'xsrf_token': cgi.escape(xsrf_token), 'payload': transforms.dumps(payload) }) }) self.assertEquals(response.status_int, 200) payload = transforms.loads(response.body) self.assertEquals(payload['status'], 200) self.assertEquals(payload['message'], 'Saved.') asset_tables = self._load_tables() self.assertEquals(asset_tables[1].find('./tbody/tr/td/a').tail, description)
def setUp(self): super(RolesTest, self).setUp() actions.login(COURSE_ADMIN_EMAIL, is_admin=True) payload_dict = { 'name': COURSE_NAME, 'title': 'Roles Test', 'admin_email': COURSE_ADMIN_EMAIL} request = { 'payload': transforms.dumps(payload_dict), 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token( 'add-course-put')} response = self.testapp.put('/rest/courses/item?%s' % urllib.urlencode( {'request': transforms.dumps(request)}), {}) self.assertEquals(response.status_int, 200) sites.setup_courses('course:/%s::ns_%s, course:/:/' % ( COURSE_NAME, COURSE_NAME)) actions.logout() config.Registry.test_overrides[roles.GCB_ADMIN_LIST.name] = ( '[%s]' % SITE_ADMIN_EMAIL) # pylint: disable-msg=protected-access self.old_registered_permission = roles.Roles._REGISTERED_PERMISSIONS roles.Roles._REGISTERED_PERMISSIONS = {} config.Registry.test_overrides[models.CAN_USE_MEMCACHE.name] = True
def fill_values(app_context, template_values, job): """Returns Jinja markup for question stats analytics.""" stats_calculated = False stats = None course = courses.Course(None, app_context=app_context) unit_id_to_title = dict() for unit in course.get_units(): if not unit.is_custom_unit(): continue if unit.custom_unit_type != base.ProgAssignment.UNIT_TYPE_ID: continue unit_id_to_title[str(unit.unit_id)] = '%s (%d)' % ( unit.title, unit.unit_id) def ordering(a1, a2): return cmp(a1['unit_id'], a2['unit_id']) if job and job.status_code == jobs.STATUS_CODE_COMPLETED: o = jobs.MapReduceJob.get_results(job) if o: stats = list(o) stats.sort(ordering) stats_calculated = True template_values.update({ 'test_stats': transforms.dumps(stats), 'unit_title': transforms.dumps(unit_id_to_title), 'stats_calculated': stats_calculated, })
def test_adding_empty_question_group(self): QG_URL = '/%s%s' % ( self.COURSE_NAME, question_group_editor.QuestionGroupRESTHandler.URI) xsrf_token = crypto.XsrfTokenManager.create_xsrf_token( question_group_editor.QuestionGroupRESTHandler.XSRF_TOKEN) description = 'Question Group' version = ( question_group_editor.QuestionGroupRESTHandler.SCHEMA_VERSIONS[0]) payload = { 'description': description, 'version': version, 'introduction': '', 'items': [] } response = self.put(QG_URL, {'request': transforms.dumps({ 'xsrf_token': cgi.escape(xsrf_token), 'payload': transforms.dumps(payload)})}) self.assertEquals(response.status_int, 200) payload = transforms.loads(response.body) self.assertEquals(payload['status'], 200) self.assertEquals(payload['message'], 'Saved.') question_groups_table = self._soup_table() self.assertEquals( question_groups_table.select('.description-cell a')[0].text.strip(), description)
def test_add_file_in_subdir(self): # Upload file via REST POST xsrf_token = crypto.XsrfTokenManager.create_xsrf_token( filer.TextAssetRESTHandler.XSRF_TOKEN_NAME) key = 'assets/lib/my_project/foo.js' contents = 'alert("Hello, world");' response = self.put( TEXT_ASSET_URL, { 'request': transforms.dumps( { 'xsrf_token': cgi.escape(xsrf_token), 'key': key, 'payload': transforms.dumps({'contents': contents}) }) }) payload = transforms.loads(response.body) self.assertEquals(200, payload['status']) # Verify that content is available via REST GET. response = self.get(TEXT_ASSET_URL + '?key=%s' % cgi.escape(key)) payload = transforms.loads(response.body) self.assertEquals(200, payload['status']) payload = transforms.loads(payload['payload']) self.assertEquals(contents, payload['contents']) # Verify that file uploaded to subdir is available via HTTP server response = self.get('/%s/%s' % (COURSE_NAME, key)) self.assertEquals(contents, response.body)
def setUp(self): super(RolesTest, self).setUp() actions.login(COURSE_ADMIN_EMAIL, is_admin=True) payload_dict = { 'name': COURSE_NAME, 'title': 'Roles Test', 'admin_email': COURSE_ADMIN_EMAIL } request = { 'payload': transforms.dumps(payload_dict), 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token('add-course-put') } response = self.testapp.put( '/rest/courses/item?%s' % urllib.urlencode({'request': transforms.dumps(request)}), {}) self.assertEquals(response.status_int, 200) sites.setup_courses('course:/%s::ns_%s, course:/:/' % (COURSE_NAME, COURSE_NAME)) actions.logout() config.Registry.test_overrides[roles.GCB_ADMIN_LIST.name] = ( '[%s]' % SITE_ADMIN_EMAIL) # pylint: disable-msg=protected-access self.old_registered_permission = roles.Roles._REGISTERED_PERMISSIONS roles.Roles._REGISTERED_PERMISSIONS = {} config.Registry.test_overrides[models.CAN_USE_MEMCACHE.name] = True
def setUp(self): super(WhitelistTest, self).setUp() config.Registry.test_overrides[ course_explorer.GCB_ENABLE_COURSE_EXPLORER_PAGE.name] = True actions.login(ADMIN_EMAIL, is_admin=True) payload_dict = { 'name': COURSE_NAME, 'title': 'Whitelist Test', 'admin_email': ADMIN_EMAIL } request = { 'payload': transforms.dumps(payload_dict), 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token('add-course-put') } response = self.testapp.put( '/rest/courses/item?%s' % urllib.urlencode({'request': transforms.dumps(request)}), {}) self.assertEquals(response.status_int, 200) sites.setup_courses('course:/%s::ns_%s, course:/:/' % (COURSE_NAME, COURSE_NAME)) actions.logout()
def test_set_hook_content(self): html_text = "<table><tbody><tr><th>;<></th></tr></tbody></table>" response = self.put( ADMIN_SETTINGS_URL, { "request": transforms.dumps( { "xsrf_token": cgi.escape(self.xsrf_token), "key": "base.after_body_tag_begins", "payload": transforms.dumps({"hook_content": html_text}), } ) }, ) self.assertEquals(200, response.status_int) response = transforms.loads(response.body) self.assertEquals(200, response["status"]) self.assertEquals("Saved.", response["message"]) # And verify that the changed text appears on course pages. # NOTE that text is as-is; no escaping of special HTML # characters should have been done. response = self.get(BASE_URL) self.assertIn(html_text, response.body)
def add_header_diagrams(handler, app_context, unit, lesson, student): # Make sure that checkbox in Skills dashboard section is checked env = courses.Course.get_environ(app_context) if SETTINGS_SCHEMA_SECTION_NAME not in env: return None if not env[SETTINGS_SCHEMA_SECTION_NAME].get(SETTING_LOCAL_GRAPH_ENABLED): return None if isinstance(student, models.TransientStudent): my_skill_map = skill_map.SkillMap.load(handler.get_course()) else: my_skill_map = skill_map.SkillMap.load(handler.get_course(), user_id=student.user_id) skills = my_skill_map.get_skills_for_lesson(lesson.lesson_id) skills_set = set(skills) # We convert this to a set for O(1) lookup time. course = handler.get_course() nodes = [ get_node_data(course, my_skill_map, skill, student) for skill in skills ] edges = [] for node in nodes: node['highlight'] = True for index in xrange(len(skills)): skill = skill_map.filter_visible_lessons(handler, student, skills[index]) prerequisites = skill.prerequisites for prereq_skill in prerequisites: if prereq_skill not in skills_set: # Add 0-based link index. The source is the node that's about to # be placed at the end, and the target is the one at the current # index. edges.append({'source': len(nodes), 'target': index}) nodes.append( get_node_data(course, my_skill_map, prereq_skill, student)) successors = my_skill_map.successors(skill) for succ_skill in successors: if succ_skill not in skills_set: # Add 0-based link index. The source is the node at the current # index, and the target is the one that's about to be placed at # the end. edges.append({'source': index, 'target': len(nodes)}) nodes.append( get_node_data(course, my_skill_map, succ_skill, student)) template_values = { 'nodes': transforms.dumps(nodes), 'edges': transforms.dumps(edges) } title = 'Skill graph' content = jinja2.Markup( handler.get_template('unit_header.html', [TEMPLATES_DIR]).render(template_values)) return {'title': title, 'content': content}
def fill_values(app_context, template_values, job): accumulated_question_answers, accumulated_assessment_answers = ( transforms.loads(job.output)) template_values['accumulated_question_answers'] = transforms.dumps( accumulated_question_answers) template_values['accumulated_assessment_answers'] = transforms.dumps( accumulated_assessment_answers)
def _fill_completed_values(self, job, template_values): accumulated_question_answers, accumulated_assessment_answers = ( transforms.loads(job.output)) template_values['accumulated_question_answers'] = transforms.dumps( accumulated_question_answers) template_values['accumulated_assessment_answers'] = transforms.dumps( accumulated_assessment_answers)
def post(self): name = COURSE_EXPLORER_SETTINGS.name request = transforms.loads(self.request.get('request')) if not self.assert_xsrf_token_or_fail(request, self.ACTION, {}): return if not roles.Roles.is_course_admin(self.app_context): transforms.send_json_response(self, 401, 'Access denied.', {}) return raw_data = transforms.loads(request.get('payload')) raw_data.pop('logo', None) try: data = transforms.json_to_dict( raw_data, schema_provider(None).get_json_schema_dict()) except (TypeError, ValueError) as err: self.validation_error(err.replace('\n', ' ')) return logo = self.request.POST.get('logo') logo_uploaded = isinstance(logo, cgi.FieldStorage) if logo_uploaded: data['logo_bytes_base64'] = base64.b64encode(logo.file.read()) data['logo_mime_type'] = logo.type with common_utils.Namespace(appengine_config.DEFAULT_NAMESPACE_NAME): entity = config.ConfigPropertyEntity.get_by_key_name(name) if entity is None: entity = config.ConfigPropertyEntity(key_name=name) old_value = None else: old_value = entity.value # Don't delete the logo. if not logo_uploaded and old_value: old_dict = transforms.loads(old_value) if ('logo_bytes_base64' in old_dict and 'logo_mime_type' in old_dict): data['logo_bytes_base64'] = old_dict['logo_bytes_base64'] data['logo_mime_type'] = old_dict['logo_mime_type'] entity.value = transforms.dumps(data) entity.is_draft = False entity.put() # is this necessary? models.EventEntity.record( 'put-property', users.get_current_user(), transforms.dumps({ 'name': name, 'before': str(old_value), 'after': str(entity.value) })) transforms.send_file_upload_response(self, 200, 'Saved.')
def fill_values(app_context, template_values, job): # pylint: disable=unpacking-non-sequence accumulated_question_answers, accumulated_assessment_answers = ( transforms.loads(job.output)) template_values['accumulated_question_answers'] = transforms.dumps( accumulated_question_answers) template_values['accumulated_assessment_answers'] = transforms.dumps( accumulated_assessment_answers)
def post(self): name = COURSE_EXPLORER_SETTINGS.name request = transforms.loads(self.request.get('request')) if not self.assert_xsrf_token_or_fail( request, self.ACTION, {}): return if not roles.Roles.is_course_admin(self.app_context): transforms.send_json_response( self, 401, 'Access denied.', {}) return raw_data = transforms.loads(request.get('payload')) raw_data.pop('logo', None) try: data = transforms.json_to_dict( raw_data, schema_provider(None).get_json_schema_dict()) except (TypeError, ValueError) as err: self.validation_error(err.replace('\n', ' ')) return logo = self.request.POST.get('logo') logo_uploaded = isinstance(logo, cgi.FieldStorage) if logo_uploaded: data['logo_bytes_base64'] = base64.b64encode(logo.file.read()) data['logo_mime_type'] = logo.type with common_utils.Namespace(appengine_config.DEFAULT_NAMESPACE_NAME): entity = config.ConfigPropertyEntity.get_by_key_name(name) if entity is None: entity = config.ConfigPropertyEntity(key_name=name) old_value = None else: old_value = entity.value # Don't delete the logo. if not logo_uploaded and old_value: old_dict = transforms.loads(old_value) if ( 'logo_bytes_base64' in old_dict and 'logo_mime_type' in old_dict): data['logo_bytes_base64'] = old_dict['logo_bytes_base64'] data['logo_mime_type'] = old_dict['logo_mime_type'] entity.value = transforms.dumps(data) entity.is_draft = False entity.put() # is this necessary? models.EventEntity.record( 'put-property', users.get_current_user(), transforms.dumps({ 'name': name, 'before': str(old_value), 'after': str(entity.value)})) transforms.send_file_upload_response(self, 200, 'Saved.')
def get_markup(self, job): """Returns Jinja markup for question stats analytics.""" errors = [] stats_calculated = False update_message = safe_dom.Text('') accumulated_question_answers = None accumulated_assessment_answers = None if not job: update_message = safe_dom.Text( 'Multiple-choice question statistics have not been calculated ' 'yet.') else: if job.status_code == jobs.STATUS_CODE_COMPLETED: accumulated_question_answers, accumulated_assessment_answers = ( transforms.loads(job.output)) stats_calculated = True update_message = safe_dom.Text( """ Multiple-choice question 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 multiple-choice question 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( """ Multiple-choice question 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( 'question_stats.html', [os.path.dirname(__file__)]).render( { 'errors': errors, 'accumulated_question_answers': transforms.dumps(accumulated_question_answers), 'accumulated_assessment_answers': transforms.dumps(accumulated_assessment_answers), 'stats_calculated': stats_calculated, 'update_message': update_message, }, autoescape=True))
def test_malformed_requests(self): response = self.put(ADMIN_SETTINGS_URL, {}) payload = transforms.loads(response.body) self.assertEquals(400, payload["status"]) self.assertEquals('Missing "request" parameter.', payload["message"]) response = self.put(ADMIN_SETTINGS_URL, {"request": "asdfasdf"}) payload = transforms.loads(response.body) self.assertEquals(400, payload["status"]) self.assertEquals('Malformed "request" parameter.', payload["message"]) response = self.put( ADMIN_SETTINGS_URL, {"request": transforms.dumps({"xsrf_token": cgi.escape(self.xsrf_token)})} ) payload = transforms.loads(response.body) self.assertEquals(400, payload["status"]) self.assertEquals('Request missing "key" parameter.', payload["message"]) response = self.put( ADMIN_SETTINGS_URL, { "request": transforms.dumps( {"xsrf_token": cgi.escape(self.xsrf_token), "key": "base:after_body_tag_begins"} ) }, ) payload = transforms.loads(response.body) self.assertEquals(400, payload["status"]) self.assertEquals('Request missing "payload" parameter.', payload["message"]) response = self.put( ADMIN_SETTINGS_URL, { "request": transforms.dumps( { "xsrf_token": cgi.escape(self.xsrf_token), "key": "base:after_body_tag_begins", "payload": "asdfsdfasdf", } ) }, ) payload = transforms.loads(response.body) self.assertEquals(400, payload["status"]) self.assertEquals('Malformed "payload" parameter.', payload["message"]) response = self.put( ADMIN_SETTINGS_URL, { "request": transforms.dumps( {"xsrf_token": cgi.escape(self.xsrf_token), "key": "base:after_body_tag_begins", "payload": "{}"} ) }, ) payload = transforms.loads(response.body) self.assertEquals(400, payload["status"]) self.assertEquals('Payload missing "hook_content" parameter.', payload["message"])
def add_header_diagrams(handler, app_context, unit, lesson, student): # Make sure that checkbox in Skills dashboard section is checked env = courses.Course.get_environ(app_context) if SETTINGS_SCHEMA_SECTION_NAME not in env: return None if not env[SETTINGS_SCHEMA_SECTION_NAME].get(SETTING_LOCAL_GRAPH_ENABLED): return None if isinstance(student, models.TransientStudent): my_skill_map = skill_map.SkillMap.load(handler.get_course()) else: my_skill_map = skill_map.SkillMap.load( handler.get_course(), user_id=student.user_id) skills = my_skill_map.get_skills_for_lesson(lesson.lesson_id) skills_set = set(skills) # We convert this to a set for O(1) lookup time. course = handler.get_course() nodes = [get_node_data(course, my_skill_map, skill, student) for skill in skills] edges = [] for node in nodes: node['highlight'] = True for index in xrange(len(skills)): skill = skill_map.filter_visible_lessons(handler, student, skills[index]) prerequisites = skill.prerequisites for prereq_skill in prerequisites: if prereq_skill not in skills_set: # Add 0-based link index. The source is the node that's about to # be placed at the end, and the target is the one at the current # index. edges.append({'source': len(nodes), 'target': index}) nodes.append(get_node_data(course, my_skill_map, prereq_skill, student)) successors = my_skill_map.successors(skill) for succ_skill in successors: if succ_skill not in skills_set: # Add 0-based link index. The source is the node at the current # index, and the target is the one that's about to be placed at # the end. edges.append({'source': index, 'target': len(nodes)}) nodes.append(get_node_data(course, my_skill_map, succ_skill, student)) template_values = {'nodes': transforms.dumps(nodes), 'edges': transforms.dumps(edges)} title = 'Skill graph' content = jinja2.Markup( handler.get_template('unit_header.html', [TEMPLATES_DIR] ).render(template_values)) return {'title': title, 'content': content}
def test_nested_json_flattens_correctly(self): dict1 = dict(aaa=111) dict2 = dict(aa=11, bb=22, cc=transforms.dumps(dict1)) dict3 = dict(a=transforms.dumps(dict2), b=2) json = transforms.loads(transforms.dumps(dict3)) flattened_json = mapreduce.CsvGenerator._flatten_json(json) result_json = transforms.loads( transforms.dumps( {'a_aa': '11', 'a_bb': '22', 'b': '2', 'a_cc_aaa': '111'})) self.assertEquals(result_json, flattened_json)
def post_settings(self, payload, upload_files=None): response = self.post('rest/explorer-settings', { 'request': transforms.dumps({ 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token( 'explorer-settings-rest'), 'payload': transforms.dumps(payload), }) }, upload_files=upload_files) self.assertEqual(response.status_code, 200) return response
def test_admin_post_change_report_allowed(self): xsrf_token = crypto.XsrfTokenManager.create_xsrf_token( 'config_override') response = self.post( '/admin?action=config_override&name=%s' % config.REPORT_ALLOWED.name, {'xsrf_token': xsrf_token}) response = self.get('/rest/config/item?key=%s' % config.REPORT_ALLOWED.name) payload = { 'name': config.REPORT_ALLOWED.name, 'value': True, 'is_draft': False, } message = { 'key': config.REPORT_ALLOWED.name, 'payload': transforms.dumps(payload), 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token( 'config-property-put'), } response = self.put('/rest/config/item', {'request': transforms.dumps(message)}) self.assertEqual(200, response.status_int) payload = { 'name': config.REPORT_ALLOWED.name, 'value': False, 'is_draft': False, } message = { 'key': config.REPORT_ALLOWED.name, 'payload': transforms.dumps(payload), 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token( 'config-property-put'), } response = self.put('/rest/config/item', {'request': transforms.dumps(message)}) self.assertEqual(200, response.status_int) expected = [{ messaging.Message._INSTALLATION: FAKE_INSTALLATION_ID, messaging.Message._TIMESTAMP: FAKE_TIMESTAMP, messaging.Message._VERSION: os.environ['GCB_PRODUCT_VERSION'], messaging.Message._METRIC: messaging.Message.METRIC_REPORT_ALLOWED, messaging.Message._VALUE: True, messaging.Message._SOURCE: messaging.Message.ADMIN_SOURCE, }, { messaging.Message._INSTALLATION: FAKE_INSTALLATION_ID, messaging.Message._TIMESTAMP: FAKE_TIMESTAMP, messaging.Message._VERSION: os.environ['GCB_PRODUCT_VERSION'], messaging.Message._METRIC: messaging.Message.METRIC_REPORT_ALLOWED, messaging.Message._VALUE: False, messaging.Message._SOURCE: messaging.Message.ADMIN_SOURCE, }] self.assertEquals(expected, MockSender.get_sent())
def test_hook_rest_edit_removes_from_old_location(self): actions.update_course_config(COURSE_NAME, {'html_hooks': {'foo': {'bar': 'zab'}}}) actions.update_course_config(COURSE_NAME, {'foo': {'bar': 'baz'}}) response = self.put(ADMIN_SETTINGS_URL, {'request': transforms.dumps({ 'xsrf_token': cgi.escape(self.xsrf_token), 'key': 'foo.bar', 'payload': transforms.dumps({'hook_content': 'BAZ'})})}) env = self.course.get_environ(self.app_context) self.assertNotIn('bar', env['foo']) self.assertEquals('BAZ', env['html_hooks']['foo']['bar'])
def serialize(self): adict = dict() adict['status'] = self.status adict['reason'] = self.reason adict['compilation_errors'] = transforms.dumps(self.compilation_errors) adict['num_test_evaluated'] = self.num_test_evaluated adict['num_test_passed'] = self.num_test_passed adict['summary'] = self.summary adict['score'] = self.score adict['test_case_results'] = [ a.to_dict() for a in self.test_case_results] return transforms.dumps(adict)
def _post(self, xsrf_token=None, payload=None): request = {} if xsrf_token is None: xsrf_token = crypto.XsrfTokenManager.create_xsrf_token(oeditor.EditorPrefsRestHandler.XSRF_TOKEN) request["xsrf_token"] = xsrf_token if payload is None: payload = {"location": self.location, "key": self.key, "state": self.EDITOR_STATE} request["payload"] = transforms.dumps(payload) data = {"request": transforms.dumps(request)} return self.post("oeditor/rest/editor_prefs", data, expect_errors=True)
def get_markup(self, job): """Returns Jinja markup for question stats analytics.""" errors = [] stats_calculated = False update_message = safe_dom.Text('') accumulated_question_answers = None accumulated_assessment_answers = None if not job: update_message = safe_dom.Text( 'Multiple-choice question statistics have not been calculated ' 'yet.') else: if job.status_code == jobs.STATUS_CODE_COMPLETED: accumulated_question_answers, accumulated_assessment_answers = ( transforms.loads(job.output)) stats_calculated = True update_message = safe_dom.Text(""" Multiple-choice question 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 multiple-choice question 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(""" Multiple-choice question 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( 'question_stats.html', [os.path.dirname(__file__)] ).render({ 'errors': errors, 'accumulated_question_answers': transforms.dumps( accumulated_question_answers), 'accumulated_assessment_answers': transforms.dumps( accumulated_assessment_answers), 'stats_calculated': stats_calculated, 'update_message': update_message, }, autoescape=True))
def _put_translation(self, data, locale, title, html): resource_key = str(i18n_dashboard.ResourceBundleKey( announcements.ResourceHandlerAnnouncement.TYPE, db.Key(encoded=data['key']).id(), locale)) xsrf_token = crypto.XsrfTokenManager.create_xsrf_token( i18n_dashboard.TranslationConsoleRestHandler.XSRF_TOKEN_NAME) request = { 'key': resource_key, 'xsrf_token': xsrf_token, 'validate': False, 'payload': transforms.dumps({ 'title': 'Announcements', 'key': resource_key, 'source_locale': 'en_US', 'target_locale': locale, 'sections': [ { 'name': 'title', 'label': 'Title', 'type': 'string', 'source_value': '', 'data': [{ 'source_value': data['title'], 'target_value': title, 'verb': 1, # verb NEW 'old_source_value': '', 'changed': True }] }, { 'name': 'html', 'label': 'Body', 'type': 'html', 'source_value': '', 'data': [{ 'source_value': data['html'], 'target_value': html, 'verb': 1, # verb NEW 'old_source_value': '', 'changed': True }] } ] }) } response = self.put( self.base + i18n_dashboard.TranslationConsoleRestHandler.URL.lstrip(), {'request': transforms.dumps(request)}) self.assertEquals(200, response.status_int) response = transforms.loads(response.body) self.assertEquals(200, response['status'])
def _initiate_import(self): # GET and then POST to the clone-a-course REST handler. response = transforms.loads(self.get(self.import_start_url).body) payload = transforms.loads(response['payload']) content = transforms.dumps({ 'course': payload['course'], }) request = transforms.dumps({ 'xsrf_token': response['xsrf_token'], 'payload': content, }) response = self.put(self.import_start_url, {'request': request}) self.assertEquals(transforms.loads(response.body)['status'], 200)
def get_data(self, key=None, xsrf_token=None): if key is None: key = self.key if xsrf_token is None: xsrf_token = utils.XsrfTokenManager.create_xsrf_token('rating') request = { 'xsrf_token': xsrf_token, 'payload': transforms.dumps({'key': key}) } return transforms.loads( self.get('rest/modules/rating?%s' % urllib.urlencode( {'request': transforms.dumps(request)})).body)
def test_add_file_in_unsupported_dir(self): # Upload file via REST POST xsrf_token = crypto.XsrfTokenManager.create_xsrf_token( filer.TextAssetRESTHandler.XSRF_TOKEN_NAME) key = 'assets/unsupported/foo.js' contents = 'alert("Hello, world");' response = self.put(TEXT_ASSET_URL, {'request': transforms.dumps({ 'xsrf_token': cgi.escape(xsrf_token), 'key': key, 'payload': transforms.dumps({'contents': contents})})}) payload = transforms.loads(response.body) self.assertEquals(400, payload['status'])
def update_test_case_stats(cls, student, unit, is_public, stats): out = dict() for index, stat in enumerate(stats): out[index] = (stat.passed, stat.reason) entity = ProgrammingTestCasesEntity.get(student, unit.unit_id) if entity is None: entity = ProgrammingTestCasesEntity.create(student, unit.unit_id) if is_public: entity.public_test_case_data = transforms.dumps(out) else: entity.private_test_case_data = transforms.dumps(out) entity.put()
def test_add_new_hook_to_page(self): hook_name = "html.my_new_hook" html_text = "<table><tbody><tr><th>;<></th></tr></tbody></table>" key = "views/base.html" url = "%s?key=%s" % (TEXT_ASSET_URL, cgi.escape(key)) # Get base page template. response = transforms.loads(self.get(url).body) xsrf_token = response["xsrf_token"] payload = transforms.loads(response["payload"]) contents = payload["contents"] # Add hook specification to page content. contents = contents.replace( '<body data-gcb-page-locale="{{ page_locale }}">', '<body data-gcb-page-locale="{{ page_locale }}">\n' + "{{ html_hooks.insert('%s') | safe }}" % hook_name, ) self.put( TEXT_ASSET_URL, { "request": transforms.dumps( { "xsrf_token": cgi.escape(xsrf_token), "key": key, "payload": transforms.dumps({"contents": contents}), } ) }, ) # Verify that new hook appears on page. response = self.get(BASE_URL) self.assertIn('id="%s"' % re.sub("[^a-zA-Z-]", "-", hook_name), response.body) # Verify that modified hook content appears on page response = self.put( ADMIN_SETTINGS_URL, { "request": transforms.dumps( { "xsrf_token": cgi.escape(self.xsrf_token), "key": hook_name, "payload": transforms.dumps({"hook_content": html_text}), } ) }, ) response = self.get(BASE_URL) self.assertIn(html_text, response.body)
def test_nested_json_flattens_correctly(self): dict1 = dict(aaa=111) dict2 = dict(aa=11, bb=22, cc=transforms.dumps(dict1)) dict3 = dict(a=transforms.dumps(dict2), b=2) json = transforms.loads(transforms.dumps(dict3)) flattened_json = mapreduce.CSVGenerator._flatten_json(json) result_json = transforms.loads( transforms.dumps({ 'a_aa': '11', 'a_bb': '22', 'b': '2', 'a_cc_aaa': '111' })) self.assertEquals(result_json, flattened_json)
def test_manipulate_non_default_item(self): html_text = '<table><tbody><tr><th>;<></th></tr></tbody></table>' new_hook_name = 'html.some_new_hook' # Verify that content prior to setting is blank. url = '%s?key=%s&xsrf_token=%s' % (ADMIN_SETTINGS_URL, cgi.escape(new_hook_name), cgi.escape(self.xsrf_token)) response = transforms.loads(self.get(url).body) payload = transforms.loads(response['payload']) self.assertIsNone(payload['hook_content']) # Set the content. response = self.put( ADMIN_SETTINGS_URL, { 'request': transforms.dumps( { 'xsrf_token': cgi.escape(self.xsrf_token), 'key': new_hook_name, 'payload': transforms.dumps( {'hook_content': html_text}) }) }) self.assertEquals(200, response.status_int) response = transforms.loads(response.body) self.assertEquals(200, response['status']) self.assertEquals('Saved.', response['message']) # Verify that content after setting is as expected url = '%s?key=%s&xsrf_token=%s' % (ADMIN_SETTINGS_URL, cgi.escape(new_hook_name), cgi.escape(self.xsrf_token)) response = transforms.loads(self.get(url).body) payload = transforms.loads(response['payload']) self.assertEquals(html_text, payload['hook_content']) # Delete the content. response = transforms.loads(self.delete(url).body) self.assertEquals(200, response['status']) self.assertEquals('Deleted.', response['message']) # Verify that content after setting is None. url = '%s?key=%s&xsrf_token=%s' % (ADMIN_SETTINGS_URL, cgi.escape(new_hook_name), cgi.escape(self.xsrf_token)) response = transforms.loads(self.get(url).body) payload = transforms.loads(response['payload']) self.assertIsNone(payload['hook_content'])
def test_events_handler_processes_normally_completed_items(self): url = '/%s/rest/events' % COURSE_NAME payload = transforms.dumps({ 'location': 'http://localhost:8081/%s/unit?unit=%s&lesson=%s' % ( COURSE_NAME, self._unit_two.unit_id, self._lesson_2_2.lesson_id) }) request = transforms.dumps({ 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token( 'event-post'), 'source': 'attempt-lesson', 'payload': payload }) response = self.post(url, {'request': request}) response = self._get_lesson(self._lesson_2_2.lesson_id) self._expect_payload(response, 2)
def _put(self, payload, expect_error=False): params = { 'request': transforms.dumps({ 'xsrf_token': crypto.XsrfTokenManager.create_xsrf_token( unit_lesson_editor.UnitLessonTitleRESTHandler.XSRF_TOKEN), 'payload': transforms.dumps(payload), }) } response = self.put(self.url, params) self.assertEquals(200, response.status_int) payload = transforms.loads(response.body) if expect_error: self.assertEquals(401, payload['status']) else: self.assertEquals(200, payload['status'])
def test_add_new_hook_to_page(self): hook_name = 'html.my_new_hook' html_text = '<table><tbody><tr><th>;<></th></tr></tbody></table>' key = 'views/base.html' url = '%s?key=%s' % (TEXT_ASSET_URL, cgi.escape(key)) # Get base page template. response = transforms.loads(self.get(url).body) xsrf_token = response['xsrf_token'] payload = transforms.loads(response['payload']) contents = payload['contents'] # Add hook specification to page content. contents = contents.replace( '<body data-gcb-page-locale="{{ page_locale }}">', '<body data-gcb-page-locale="{{ page_locale }}">\n' + '{{ html_hooks.insert(\'%s\') | safe }}' % hook_name) self.put( TEXT_ASSET_URL, { 'request': transforms.dumps( { 'xsrf_token': cgi.escape(xsrf_token), 'key': key, 'payload': transforms.dumps({'contents': contents}) }) }) # Verify that new hook appears on page. response = self.get(BASE_URL) self.assertIn('id="%s"' % re.sub('[^a-zA-Z-]', '-', hook_name), response.body) # Verify that modified hook content appears on page response = self.put( ADMIN_SETTINGS_URL, { 'request': transforms.dumps( { 'xsrf_token': cgi.escape(self.xsrf_token), 'key': hook_name, 'payload': transforms.dumps( {'hook_content': html_text}) }) }) response = self.get(BASE_URL) self.assertIn(html_text, response.body)
def test_not_enough_assignments_to_allocate(self): """Test for the case when there are too few assignments in the pool.""" email = '*****@*****.**' name = 'Student 1' submission = transforms.dumps([ {'index': 0, 'type': 'regex', 'value': 'S1-1', 'correct': True}, {'index': 1, 'type': 'choices', 'value': 3, 'correct': False}, {'index': 2, 'type': 'regex', 'value': 'is-S1', 'correct': True}, ]) payload = { 'answers': submission, 'assessment_type': LEGACY_REVIEW_UNIT_ID} actions.login(email) actions.register(self, name) response = actions.submit_assessment( self, LEGACY_REVIEW_UNIT_ID, payload) # The student goes to the review dashboard and requests an assignment # to review -- but there is nothing to review. response = actions.request_new_review( self, LEGACY_REVIEW_UNIT_ID, expected_status_code=200) assert_does_not_contain('Assignment to review', response.body) assert_contains('Sorry, there are no new submissions ', response.body) assert_contains('disabled="true"', response.body) actions.logout()
def post_config_reset(self): """Handles 'reset' property action.""" name = self.request.get('name') # Find item in registry. item = None if name and name in config.Registry.registered.keys(): item = config.Registry.registered[name] if not item: self.redirect('/admin?action=settings') # Delete if exists. try: entity = config.ConfigPropertyEntity.get_by_key_name(name) if entity: old_value = entity.value entity.delete() models.EventEntity.record( 'delete-property', users.get_current_user(), transforms.dumps({ 'name': name, 'value': str(old_value)})) except db.BadKeyError: pass self.redirect('/admin?action=settings')
def post_config_override(self): """Handles 'override' property action.""" name = self.request.get('name') # Find item in registry. item = None if name and name in config.Registry.registered.keys(): item = config.Registry.registered[name] if not item: self.redirect('/admin?action=settings') # Add new entity if does not exist. try: entity = config.ConfigPropertyEntity.get_by_key_name(name) except db.BadKeyError: entity = None if not entity: entity = config.ConfigPropertyEntity(key_name=name) entity.value = str(item.value) entity.is_draft = True entity.put() models.EventEntity.record( 'override-property', users.get_current_user(), transforms.dumps({ 'name': name, 'value': str(entity.value)})) self.redirect('/admin?%s' % urllib.urlencode( {'action': 'config_edit', 'name': name}))
def configure_active_view_1_5(self, unit, submission_contents): self.template_value['html_content'] = unit.html_content self.template_value['html_check_answers'] = unit.html_check_answers if submission_contents: # If a previous submission exists, reinstate it. self.template_value['html_saved_answers'] = transforms.dumps( submission_contents)
def _add_unit_and_content(unit, result): """Adds the score dimensions for units and its lessons and questions.""" # The content of an assessment is indicated by a lesson_id of None. # Inside that lesson we can find all the questions added directly # to the assessment. unit_dict = { DIM_TYPE: DIM_TYPE_UNIT, # Unit or assessment DIM_ID: unit["unit_id"], "name": unit["title"], } # Name won't be saved in ClusterEntity result.append(unit_dict) unit_scored_lessons = 0 for item in unit["contents"]: lesson_id = item.get("lesson_id") # A unit may have a pre or post assessment, in that case the item # has unit_id, not a lesson_id. included_assessment_id = item.get("unit_id") lesson_title = item.get("title") if lesson_title and lesson_id and item.get("tallied"): result.append({DIM_TYPE: DIM_TYPE_LESSON, DIM_ID: lesson_id, "name": lesson_title}) unit_scored_lessons += 1 elif included_assessment_id and lesson_title: result.append({DIM_TYPE: DIM_TYPE_UNIT, DIM_ID: included_assessment_id, "name": lesson_title}) unit_scored_lessons += 1 # If lesson is not tallied (graded) is not considered a dimension for question in item["questions"]: if included_assessment_id: question_id = pack_question_dimid(included_assessment_id, None, question["id"]) else: question_id = pack_question_dimid(unit["unit_id"], lesson_id, question["id"]) result.append({DIM_TYPE: DIM_TYPE_QUESTION, DIM_ID: question_id, "name": question["description"]}) # This should affect the result list as well. unit_dict[DIM_EXTRA_INFO] = transforms.dumps({"unit_scored_lessons": unit_scored_lessons})
def post_config_reset(self): """Handles 'reset' property action.""" name = self.request.get('name') # Find item in registry. item = None if name and name in config.Registry.registered.keys(): item = config.Registry.registered[name] if not item: self.redirect('%s?tab=settings' % self.LINK_URL) with Namespace(appengine_config.DEFAULT_NAMESPACE_NAME): # Delete if exists. try: entity = config.ConfigPropertyEntity.get_by_key_name(name) if entity: old_value = entity.value entity.delete() models.EventEntity.record( 'delete-property', users.get_current_user(), transforms.dumps({ 'name': name, 'value': str(old_value) })) except db.BadKeyError: pass self.redirect('%s?tab=settings' % self.URL)
def test_student_cannot_see_reviews_prematurely(self): """Test that students cannot see others' reviews prematurely.""" email = '*****@*****.**' name = 'Student 1' submission = transforms.dumps([ {'index': 0, 'type': 'regex', 'value': 'S1-1', 'correct': True}, {'index': 1, 'type': 'choices', 'value': 3, 'correct': False}, {'index': 2, 'type': 'regex', 'value': 'is-S1', 'correct': True}, ]) payload = { 'answers': submission, 'assessment_type': LEGACY_REVIEW_UNIT_ID} actions.login(email) actions.register(self, name) response = actions.submit_assessment( self, LEGACY_REVIEW_UNIT_ID, payload) # Student 1 cannot see the reviews for his assignment yet, because he # has not submitted the two required reviews. response = self.get('assessment?name=%s' % LEGACY_REVIEW_UNIT_ID) assert_equals(response.status_int, 200) assert_contains('Due date for this assignment', response.body) assert_contains( 'After you have completed the required number of peer reviews', response.body) actions.logout()
def _collect_blobstore_paths(cls, root_key): paths = set() # pylint: disable=protected-access for model, field_name in ((pipeline_models._SlotRecord, 'value'), (pipeline_models._PipelineRecord, 'params')): prev_cursor = None any_records = True while any_records: any_records = False query = (model.all().filter('root_pipeline =', root_key).with_cursor(prev_cursor)) for record in query.run(): any_records = True # The data parameters in SlotRecord and PipelineRecord # vary widely, but all are provided via this interface as # some combination of Python scalar, list, tuple, and # dict. Rather than depend on specifics of the map/reduce # internals, crush the object to a string and parse that. try: data_object = getattr(record, field_name) except TypeError: data_object = None if data_object: text = transforms.dumps(data_object) for path in re.findall(r'"(/blobstore/[^"]+)"', text): paths.add(path) prev_cursor = query.cursor() return paths
def _collect_blobstore_paths(cls, root_key): paths = set() # pylint: disable=protected-access for model, field_name in ((pipeline_models._SlotRecord, 'value'), (pipeline_models._PipelineRecord, 'params')): prev_cursor = None any_records = True while any_records: any_records = False query = (model .all() .filter('root_pipeline =', root_key) .with_cursor(prev_cursor)) for record in query.run(): any_records = True # The data parameters in SlotRecord and PipelineRecord # vary widely, but all are provided via this interface as # some combination of Python scalar, list, tuple, and # dict. Rather than depend on specifics of the map/reduce # internals, crush the object to a string and parse that. try: data_object = getattr(record, field_name) except TypeError: data_object = None if data_object: text = transforms.dumps(data_object) for path in re.findall(r'"(/blobstore/[^"]+)"', text): paths.add(path) prev_cursor = query.cursor() return paths
def configure_active_review_1_4(self, unit, review_contents): self.template_value['assessment_script_src'] = ( self.get_course().get_review_form_filename(unit.unit_id)) saved_answers = ( StudentWorkUtils.get_answer_list(review_contents) if review_contents else []) self.template_value['saved_answers'] = transforms.dumps(saved_answers)