def setUp(self): """Completes the sign-up process for the various users.""" super(BaseTopicsAndSkillsDashboardTests, self).setUp() self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.signup(self.TOPIC_MANAGER_EMAIL, self.TOPIC_MANAGER_USERNAME) self.signup(self.NEW_USER_EMAIL, self.NEW_USER_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.topic_manager_id = self.get_user_id_from_email( self.TOPIC_MANAGER_EMAIL) self.new_user_id = self.get_user_id_from_email(self.NEW_USER_EMAIL) self.set_admins([self.ADMIN_USERNAME]) self.set_topic_managers([self.TOPIC_MANAGER_USERNAME]) self.topic_id = topic_services.get_new_topic_id() self.linked_skill_id = skill_services.get_new_skill_id() self.save_new_skill(self.linked_skill_id, self.admin_id, 'Description 3') skill_services.publish_skill(self.linked_skill_id, self.admin_id) self.save_new_topic(self.topic_id, self.admin_id, 'Name', 'Description', [], [], [self.linked_skill_id], [], 1)
def test_get_with_five_or_more_questions(self): number_of_questions = 6 self.topic_id = 'new_topic' self.skill_id_1 = skill_services.get_new_skill_id() self.topic = topic_domain.Topic.create_default_topic( self.topic_id, 'new_topic', 'abbrev') self.topic.uncategorized_skill_ids.append(self.skill_id_1) self.topic.thumbnail_filename = 'Image.png' topic_services.save_new_topic(self.admin_id, self.topic) topic_services.publish_topic(self.topic_id, self.admin_id) self.save_new_skill(self.skill_id_1, self.admin_id, description='Skill Description 1') for index in python_utils.RANGE(number_of_questions): question_id = question_services.get_new_question_id() self.save_new_question(question_id, self.admin_id, self._create_valid_question_data(index), [self.skill_id_1]) question_services.create_new_question_skill_link( self.admin_id, question_id, self.skill_id_1, 0.5) with self.swap(constants, 'ENABLE_NEW_STRUCTURE_PLAYERS', True): json_response = self.get_json( '%s/%s' % (feconf.TOPIC_DATA_HANDLER, 'new_topic')) expected_dict = { 'topic_name': 'new_topic', 'topic_id': self.topic_id, 'canonical_story_dicts': [], 'additional_story_dicts': [], 'uncategorized_skill_ids': [self.skill_id_1], 'subtopics': [], 'degrees_of_mastery': { self.skill_id_1: None }, 'skill_descriptions': { self.skill_id_1: 'Skill Description 1' }, 'train_tab_should_be_displayed': True } self.assertDictContainsSubset(expected_dict, json_response) self.logout()
def setUp(self): super(SkillServicesUnitTests, self).setUp() skill_contents = skill_domain.SkillContents( state_domain.SubtitledHtml('1', '<p>Explanation</p>'), [state_domain.SubtitledHtml('2', '<p>Example 1</p>')], state_domain.RecordedVoiceovers.from_dict( {'voiceovers_mapping': { '1': {}, '2': {} }}), state_domain.WrittenTranslations.from_dict( {'translations_mapping': { '1': {}, '2': {} }})) misconceptions = [ skill_domain.Misconception(self.MISCONCEPTION_ID_1, 'name', '<p>description</p>', '<p>default_feedback</p>') ] self.SKILL_ID = skill_services.get_new_skill_id() self.signup('*****@*****.**', 'A') self.signup(self.ADMIN_EMAIL, username=self.ADMIN_USERNAME) self.signup('*****@*****.**', username='******') self.user_id_a = self.get_user_id_from_email('*****@*****.**') self.user_id_admin = self.get_user_id_from_email(self.ADMIN_EMAIL) self.user_id_admin_2 = self.get_user_id_from_email( '*****@*****.**') self.set_admins([self.ADMIN_USERNAME, 'adm2']) self.user_a = user_services.UserActionsInfo(self.user_id_a) self.user_admin = user_services.UserActionsInfo(self.user_id_admin) self.user_admin_2 = user_services.UserActionsInfo(self.user_id_admin_2) self.skill = self.save_new_skill(self.SKILL_ID, self.USER_ID, 'Description', misconceptions=misconceptions, skill_contents=skill_contents)
def test_skill_description_handler_when_duplicate(self): self.login(self.CURRICULUM_ADMIN_EMAIL) json_response = self.get_json(self.url) self.assertEqual(json_response['skill_description_exists'], False) # Publish a skill. new_skill_id = skill_services.get_new_skill_id() rubrics = [ skill_domain.Rubric(constants.SKILL_DIFFICULTIES[0], ['Explanation 1']), skill_domain.Rubric(constants.SKILL_DIFFICULTIES[1], ['Explanation 2']), skill_domain.Rubric(constants.SKILL_DIFFICULTIES[2], ['Explanation 3']) ] skill = skill_domain.Skill.create_default_skill( new_skill_id, self.skill_description, rubrics) skill_services.save_new_skill(self.admin_id, skill) # Skill description exists since we've already published it. json_response = self.get_json(self.url) self.assertEqual(json_response['skill_description_exists'], True)
def test_pre_accept_validate_change_skill_id(self): expected_suggestion_dict = self.suggestion_dict suggestion = suggestion_registry.SuggestionAddQuestion( expected_suggestion_dict['suggestion_id'], expected_suggestion_dict['target_id'], expected_suggestion_dict['target_version_at_submission'], expected_suggestion_dict['status'], self.author_id, self.reviewer_id, expected_suggestion_dict['change'], expected_suggestion_dict['score_category'], self.fake_date) skill_id = skill_services.get_new_skill_id() self.save_new_skill(skill_id, self.author_id, 'description') suggestion.change.skill_id = skill_id suggestion.pre_accept_validate() suggestion.change.skill_id = None with self.assertRaisesRegexp( Exception, 'Expected change to contain skill_id'): suggestion.pre_accept_validate()
def post(self): if not constants.ENABLE_NEW_STRUCTURE_EDITORS: raise self.PageNotFoundException description = self.payload.get('description') linked_topic_ids = self.payload.get('linked_topic_ids') new_skill_id = skill_services.get_new_skill_id() if linked_topic_ids is not None: topics = topic_services.get_topics_by_ids(linked_topic_ids) for topic in topics: if topic is None: raise self.InvalidInputException topic_services.add_uncategorized_skill(self.user_id, topic.id, new_skill_id) skill_domain.Skill.require_valid_description(description) skill = skill_domain.Skill.create_default_skill( new_skill_id, description) skill_services.save_new_skill(self.user_id, skill) self.render_json({'skillId': new_skill_id})
def setUp(self): super(SkillFetchersUnitTests, self).setUp() example_1 = skill_domain.WorkedExample( state_domain.SubtitledHtml('2', '<p>Example Question 1</p>'), state_domain.SubtitledHtml('3', '<p>Example Explanation 1</p>')) skill_contents = skill_domain.SkillContents( state_domain.SubtitledHtml('1', '<p>Explanation</p>'), [example_1], state_domain.RecordedVoiceovers.from_dict( {'voiceovers_mapping': { '1': {}, '2': {}, '3': {} }}), state_domain.WrittenTranslations.from_dict( {'translations_mapping': { '1': {}, '2': {}, '3': {} }})) misconceptions = [ skill_domain.Misconception(self.MISCONCEPTION_ID_1, 'name', '<p>description</p>', '<p>default_feedback</p>', True) ] self.SKILL_ID = skill_services.get_new_skill_id() self.signup(self.CURRICULUM_ADMIN_EMAIL, self.CURRICULUM_ADMIN_USERNAME) self.user_id_admin = (self.get_user_id_from_email( self.CURRICULUM_ADMIN_EMAIL)) self.set_curriculum_admins([self.CURRICULUM_ADMIN_USERNAME]) self.skill = self.save_new_skill( self.SKILL_ID, self.USER_ID, description='Description', misconceptions=misconceptions, skill_contents=skill_contents, prerequisite_skill_ids=['skill_id_1', 'skill_id_2'])
def test_fetch_filtered_skills_with_given_cursor(self): self.login(self.CURRICULUM_ADMIN_EMAIL) csrf_token = self.get_new_csrf_token() skill_id = skill_services.get_new_skill_id() self.save_new_skill( skill_id, self.admin_id, description='Random Skill') json_response = self.post_json( feconf.SKILL_DASHBOARD_DATA_URL, { 'num_skills_to_fetch': 1, }, csrf_token=csrf_token) # Default sort is "Newly created first". So, the skill with id-skill_id # is the most "Newly created", and therefore it comes first. The skill # with id-subtopic_skill_id was created before the above skill, # so it comes second. Then the skill with id-linked_skill_id was created # before the other two skills, hence it comes last because it is the # least "Newly Created". self.assertEqual(len(json_response['skill_summary_dicts']), 2) self.assertEqual( json_response['skill_summary_dicts'][0]['id'], skill_id) self.assertEqual( json_response['skill_summary_dicts'][1]['id'], self.subtopic_skill_id) self.assertTrue(json_response['more']) self.assertTrue( isinstance(json_response['next_cursor'], python_utils.BASESTRING)) next_cursor = json_response['next_cursor'] json_response = self.post_json( feconf.SKILL_DASHBOARD_DATA_URL, { 'num_skills_to_fetch': 1, 'next_cursor': next_cursor, }, csrf_token=csrf_token) self.assertEqual(len(json_response['skill_summary_dicts']), 1) self.assertEqual( json_response['skill_summary_dicts'][0]['id'], self.linked_skill_id)
def post(self): description = self.payload.get('description') linked_topic_ids = self.payload.get('linked_topic_ids') explanation_dict = self.payload.get('explanation_dict') rubrics = self.payload.get('rubrics') if not isinstance(rubrics, list): raise self.InvalidInputException('Rubrics should be a list.') if not isinstance(explanation_dict, dict): raise self.InvalidInputException('Explanation should be a dict.') try: state_domain.SubtitledHtml.from_dict(explanation_dict) except: raise self.InvalidInputException( 'Explanation should be a valid SubtitledHtml dict.') rubrics = [skill_domain.Rubric.from_dict(rubric) for rubric in rubrics] new_skill_id = skill_services.get_new_skill_id() if linked_topic_ids is not None: topics = topic_fetchers.get_topics_by_ids(linked_topic_ids) for topic in topics: if topic is None: raise self.InvalidInputException topic_services.add_uncategorized_skill(self.user_id, topic.id, new_skill_id) skill_domain.Skill.require_valid_description(description) skill = skill_domain.Skill.create_default_skill( new_skill_id, description, rubrics) skill.update_explanation( state_domain.SubtitledHtml.from_dict(explanation_dict)) skill_services.save_new_skill(self.user_id, skill) self.render_json({'skillId': new_skill_id})
def test_fetch_filtered_skills_with_invalid_cursor_type(self): self.login(self.CURRICULUM_ADMIN_EMAIL) csrf_token = self.get_new_csrf_token() skill_id = skill_services.get_new_skill_id() self.save_new_skill(skill_id, self.admin_id, description='Random Skill') json_response = self.post_json(feconf.SKILL_DASHBOARD_DATA_URL, { 'num_skills_to_fetch': 1, 'next_cursor': 40, 'status': 'All', 'sort': 'Newly Created', 'classroom_name': 'All', 'keywords': [] }, csrf_token=csrf_token, expected_status_int=400) expected_error = ('Schema validation for \'next_cursor\' failed: ' 'Expected string, received 40') self.assertEqual(json_response['error'], expected_error)
def post(self): if not feconf.ENABLE_NEW_STRUCTURES: raise self.PageNotFoundException topic_id = self.payload.get('topic_id') if topic_id is not None: topic = topic_services.get_topic_by_id(topic_id, strict=False) if topic is None: raise self.InvalidInputException description = self.payload.get('description') skill_domain.Skill.require_valid_description(description) new_skill_id = skill_services.get_new_skill_id() skill = skill_domain.Skill.create_default_skill( new_skill_id, description) skill_services.save_new_skill(self.user_id, skill) if topic_id is not None: topic_services.add_skill(self.user_id, topic_id, new_skill_id) self.render_json({'skillId': new_skill_id})
def setUp(self): """Before each individual test, create a dummy skill.""" super(ConceptCardDataHandlerTest, self).setUp() self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.set_admins([self.ADMIN_USERNAME]) self.skill_contents = skill_domain.SkillContents( state_domain.SubtitledHtml( '1', 'Skill Explanation'), [ state_domain.SubtitledHtml('2', 'Example 1'), state_domain.SubtitledHtml('3', 'Example 2')], {'1': {}, '2': {}, '3': {}}, state_domain.WrittenTranslations.from_dict({ 'translations_mapping': {'1': {}, '2': {}, '3': {}} })) self.admin = user_services.UserActionsInfo(self.admin_id) self.skill_id = skill_services.get_new_skill_id() self.save_new_skill( self.skill_id, self.admin_id, 'Description', skill_contents=self.skill_contents)
def test_get(self): # Check that non-admins or non-topic managers cannot access the # topics and skills dashboard data. skill_id = skill_services.get_new_skill_id() self.save_new_skill(skill_id, self.admin_id, 'Description') with self.swap(feconf, 'ENABLE_NEW_STRUCTURES', True): self.login(self.NEW_USER_EMAIL) response = self.testapp.get( '%s' % feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL, expect_errors=True) self.assertEqual(response.status_int, 401) self.logout() # Check that admins can access the topics and skills dashboard data. self.login(self.ADMIN_EMAIL) json_response = self.get_json( '%s' % feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL) self.assertEqual(len(json_response['topic_summary_dicts']), 1) self.assertEqual(json_response['topic_summary_dicts'][0]['id'], self.topic_id) self.assertEqual(len(json_response['skill_summary_dicts']), 1) self.assertEqual(json_response['skill_summary_dicts'][0]['id'], skill_id) self.logout() # Check that topic managers can access the topics and skills # dashboard editable topic data. self.login(self.ADMIN_EMAIL) json_response = self.get_json( '%s' % feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL) self.assertEqual(len(json_response['topic_summary_dicts']), 1) self.assertEqual(json_response['topic_summary_dicts'][0]['id'], self.topic_id) self.assertEqual(len(json_response['skill_summary_dicts']), 1) self.assertEqual(json_response['skill_summary_dicts'][0]['id'], skill_id) self.logout()
def _generate_dummy_skill_and_questions(self): """Generate and loads the database with a skill and 15 questions linked to the skill. Raises: Exception: Cannot load new structures data in production mode. Exception: User does not have enough rights to generate data. """ if constants.DEV_MODE: if self.user.role != feconf.ROLE_ID_ADMIN: raise Exception( 'User does not have enough rights to generate data.') skill_id = skill_services.get_new_skill_id() skill_name = 'Dummy Skill %s' % python_utils.UNICODE( random.getrandbits(32)) skill = self._create_dummy_skill(skill_id, skill_name, '<p>Dummy Explanation 1</p>') skill_services.save_new_skill(self.user_id, skill) for i in python_utils.RANGE(15): question_id = question_services.get_new_question_id() question_name = 'Question number %s %s' % ( python_utils.UNICODE(i), skill_name) question = self._create_dummy_question(question_id, question_name, [skill_id]) question_services.add_question(self.user_id, question) question_difficulty = [ feconf.EASY_SKILL_DIFFICULTY, feconf.MEDIUM_SKILL_DIFFICULTY, feconf.HARD_SKILL_DIFFICULTY ] random_difficulty = random.choice(question_difficulty) question_services.create_new_question_skill_link( self.user_id, question_id, skill_id, random_difficulty) else: raise Exception('Cannot generate dummy skills in production.')
def setUp(self): super(SkillServicesUnitTests, self).setUp() skill_contents = skill_domain.SkillContents( 'Explanation', ['Example 1']) misconceptions = [skill_domain.Misconception( self.MISCONCEPTION_ID_1, 'name', 'description', 'default_feedback')] self.SKILL_ID = skill_services.get_new_skill_id() self.signup('*****@*****.**', 'A') self.signup(self.ADMIN_EMAIL, username=self.ADMIN_USERNAME) self.signup('*****@*****.**', username='******') self.user_id_a = self.get_user_id_from_email('*****@*****.**') self.user_id_admin = self.get_user_id_from_email(self.ADMIN_EMAIL) self.user_id_admin_2 = self.get_user_id_from_email('*****@*****.**') self.set_admins([self.ADMIN_USERNAME, 'adm2']) self.user_a = user_services.UserActionsInfo(self.user_id_a) self.user_admin = user_services.UserActionsInfo(self.user_id_admin) self.user_admin_2 = user_services.UserActionsInfo(self.user_id_admin_2) self.skill = self.save_new_skill( self.SKILL_ID, self.USER_ID, 'Description', misconceptions=misconceptions, skill_contents=skill_contents)
def test_merge_skill(self): self.login(self.ADMIN_EMAIL) old_skill_id = self.linked_skill_id new_skill_id = skill_services.get_new_skill_id() self.save_new_skill(new_skill_id, self.admin_id, 'Skill Description') old_links = question_services.get_question_skill_links_of_skill( old_skill_id, 'Old Description') new_links = question_services.get_question_skill_links_of_skill( new_skill_id, 'Skill Description') self.assertEqual(len(old_links), 1) self.assertEqual(old_links[0].skill_id, old_skill_id) self.assertEqual(len(new_links), 0) with self.swap(constants, 'ENABLE_NEW_STRUCTURE_EDITORS', True): csrf_token = self._get_csrf_token_for_put() payload = { 'old_skill_id': old_skill_id, 'new_skill_id': new_skill_id } json_response = self.post_json(self.url, payload, csrf_token=csrf_token) old_links = question_services.get_question_skill_links_of_skill( old_skill_id, 'Old Description') new_links = question_services.get_question_skill_links_of_skill( new_skill_id, 'Skill Description') self.assertEqual(json_response['merged_into_skill'], new_skill_id) self.assertEqual(len(old_links), 0) self.assertEqual(len(new_links), 1) self.assertEqual(new_links[0].skill_id, new_skill_id) self.logout()
def setUp(self): """Completes the setup for SubtopicMasteryDataHandler.""" super(SubtopicMasteryDataHandlerTest, self).setUp() self.signup(self.NEW_USER_EMAIL, self.NEW_USER_USERNAME) self.user_id = self.get_user_id_from_email(self.NEW_USER_EMAIL) self.set_curriculum_admins([self.NEW_USER_USERNAME]) self.skill_id_1 = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id_1, self.user_id, description='Skill Description 1') self.skill_id_2 = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id_2, self.user_id, description='Skill Description 2') self.skill_id_3 = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id_3, self.user_id, description='Skill Description 3') self.skill_id_4 = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id_4, self.user_id, description='Skill Description 4') self.skill_id_5 = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id_5, self.user_id, description='Skill Description 5') self.skill_id_6 = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id_6, self.user_id, description='Skill Description 6') self.degree_of_mastery_1 = 0.1 self.degree_of_mastery_2 = 0.3 self.degree_of_mastery_3 = 0.5 self.degree_of_mastery_4 = 0.7 self.degree_of_mastery_5 = 0.9 self.degree_of_mastery_6 = 0.6
def setUp(self): """Completes the setup for QuestionSkillLinkHandlerTest.""" super(QuestionCreationHandlerTest, self).setUp() self.skill_id = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id, self.admin_id, 'Skill Description')
def setUp(self): """Completes the sign-up process for the various users.""" super(BaseTopicEditorControllerTests, self).setUp() self.signup(self.TOPIC_MANAGER_EMAIL, self.TOPIC_MANAGER_USERNAME) self.signup(self.NEW_USER_EMAIL, self.NEW_USER_USERNAME) self.signup(self.CURRICULUM_ADMIN_EMAIL, self.CURRICULUM_ADMIN_USERNAME) self.admin_id = self.get_user_id_from_email( self.CURRICULUM_ADMIN_EMAIL) self.topic_manager_id = self.get_user_id_from_email( self.TOPIC_MANAGER_EMAIL) self.new_user_id = self.get_user_id_from_email(self.NEW_USER_EMAIL) self.set_curriculum_admins([self.CURRICULUM_ADMIN_USERNAME]) self.topic_manager = user_services.get_user_actions_info( self.topic_manager_id) self.admin = user_services.get_user_actions_info(self.admin_id) self.new_user = user_services.get_user_actions_info(self.new_user_id) self.skill_id = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id, self.admin_id, description='Skill Description') self.skill_id_2 = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id_2, self.admin_id, description='Skill Description 2') self.topic_id = topic_fetchers.get_new_topic_id() self.save_new_topic( self.topic_id, self.admin_id, name='Name', abbreviated_name='topic-one', url_fragment='topic-one', description='Description', canonical_story_ids=[], additional_story_ids=[], uncategorized_skill_ids=[self.skill_id, self.skill_id_2], subtopics=[], next_subtopic_id=1) changelist = [ topic_domain.TopicChange({ 'cmd': topic_domain.CMD_ADD_SUBTOPIC, 'title': 'Title', 'subtopic_id': 1 }), topic_domain.TopicChange({ 'cmd': topic_domain.CMD_MOVE_SKILL_ID_TO_SUBTOPIC, 'old_subtopic_id': None, 'new_subtopic_id': 1, 'skill_id': self.skill_id }) ] topic_services.update_topic_and_subtopic_pages(self.admin_id, self.topic_id, changelist, 'Added subtopic.') self.set_topic_managers([self.TOPIC_MANAGER_USERNAME], self.topic_id) self.login(self.CURRICULUM_ADMIN_EMAIL, is_super_admin=True) csrf_token = self.get_new_csrf_token() new_config_value = [{ 'name': 'math', 'url_fragment': 'math', 'topic_ids': [self.topic_id], 'course_details': '', 'topic_list_intro': '' }] payload = { 'action': 'save_config_properties', 'new_config_property_values': { config_domain.CLASSROOM_PAGES_DATA.name: (new_config_value), } } self.post_json('/adminhandler', payload, csrf_token=csrf_token) self.logout()
def post(self): description = self.payload.get('description') linked_topic_ids = self.payload.get('linked_topic_ids') explanation_dict = self.payload.get('explanation_dict') rubrics = self.payload.get('rubrics') if not isinstance(rubrics, list): raise self.InvalidInputException('Rubrics should be a list.') if not isinstance(explanation_dict, dict): raise self.InvalidInputException('Explanation should be a dict.') try: subtitled_html = ( state_domain.SubtitledHtml.from_dict(explanation_dict)) subtitled_html.validate() except Exception as e: raise self.InvalidInputException( 'Explanation should be a valid SubtitledHtml dict.') from e rubrics = [skill_domain.Rubric.from_dict(rubric) for rubric in rubrics] new_skill_id = skill_services.get_new_skill_id() if linked_topic_ids is not None: topics = topic_fetchers.get_topics_by_ids(linked_topic_ids) for topic in topics: if topic is None: raise self.InvalidInputException topic_services.add_uncategorized_skill(self.user_id, topic.id, new_skill_id) skill_domain.Skill.require_valid_description(description) if skill_services.does_skill_with_description_exist(description): raise self.InvalidInputException( 'Skill description should not be a duplicate.') skill = skill_domain.Skill.create_default_skill( new_skill_id, description, rubrics) skill.update_explanation( state_domain.SubtitledHtml.from_dict(explanation_dict)) image_filenames = skill_services.get_image_filenames_from_skill(skill) skill_services.save_new_skill(self.user_id, skill) image_validation_error_message_suffix = ( 'Please go to oppia.org/skill_editor/%s to edit ' 'the image.' % skill.id) for filename in image_filenames: image = self.request.get(filename) if not image: logging.exception( 'Image not provided for file with name %s when the skill ' 'with id %s was created.' % (filename, skill.id)) raise self.InvalidInputException( 'No image data provided for file with name %s. %s' % (filename, image_validation_error_message_suffix)) try: file_format = ( image_validation_services.validate_image_and_filename( image, filename)) except utils.ValidationError as e: e = '%s %s' % (e, image_validation_error_message_suffix) raise self.InvalidInputException(e) image_is_compressible = (file_format in feconf.COMPRESSIBLE_IMAGE_FORMATS) fs_services.save_original_and_compressed_versions_of_image( filename, feconf.ENTITY_TYPE_SKILL, skill.id, image, 'image', image_is_compressible) self.render_json({'skillId': new_skill_id})
def _load_dummy_new_structures_data(self): """Loads the database with two topics (one of which is empty), a story and three skills in the topic (two of them in a subtopic) and a question attached to each skill. Raises: Exception: Cannot load new structures data in production mode. Exception: User does not have enough rights to generate data. """ if constants.DEV_MODE: if self.user.role != feconf.ROLE_ID_ADMIN: raise Exception( 'User does not have enough rights to generate data.') topic_id_1 = topic_services.get_new_topic_id() topic_id_2 = topic_services.get_new_topic_id() story_id = story_services.get_new_story_id() skill_id_1 = skill_services.get_new_skill_id() skill_id_2 = skill_services.get_new_skill_id() skill_id_3 = skill_services.get_new_skill_id() question_id_1 = question_services.get_new_question_id() question_id_2 = question_services.get_new_question_id() question_id_3 = question_services.get_new_question_id() skill_1 = self._create_dummy_skill(skill_id_1, 'Dummy Skill 1', '<p>Dummy Explanation 1</p>') skill_2 = self._create_dummy_skill(skill_id_2, 'Dummy Skill 2', '<p>Dummy Explanation 2</p>') skill_3 = self._create_dummy_skill(skill_id_3, 'Dummy Skill 3', '<p>Dummy Explanation 3</p>') question_1 = self._create_dummy_question(question_id_1, 'Question 1', [skill_id_1]) question_2 = self._create_dummy_question(question_id_2, 'Question 2', [skill_id_2]) question_3 = self._create_dummy_question(question_id_3, 'Question 3', [skill_id_3]) question_services.add_question(self.user_id, question_1) question_services.add_question(self.user_id, question_2) question_services.add_question(self.user_id, question_3) question_services.create_new_question_skill_link( self.user_id, question_id_1, skill_id_1, 0.3) question_services.create_new_question_skill_link( self.user_id, question_id_2, skill_id_2, 0.5) question_services.create_new_question_skill_link( self.user_id, question_id_3, skill_id_3, 0.7) topic_1 = topic_domain.Topic.create_default_topic( topic_id_1, 'Dummy Topic 1', 'abbrev') topic_2 = topic_domain.Topic.create_default_topic( topic_id_2, 'Empty Topic', 'abbrev') topic_1.add_canonical_story(story_id) topic_1.add_uncategorized_skill_id(skill_id_1) topic_1.add_uncategorized_skill_id(skill_id_2) topic_1.add_uncategorized_skill_id(skill_id_3) topic_1.add_subtopic(1, 'Dummy Subtopic Title') topic_1.move_skill_id_to_subtopic(None, 1, skill_id_2) topic_1.move_skill_id_to_subtopic(None, 1, skill_id_3) subtopic_page = ( subtopic_page_domain.SubtopicPage.create_default_subtopic_page( 1, topic_id_1)) self._reload_exploration('0') self._reload_exploration('16') story = story_domain.Story.create_default_story( story_id, 'Dummy Story 1', topic_id_1) story.add_node('%s%d' % (story_domain.NODE_ID_PREFIX, 1), 'Dummy Chapter 1') story.update_node_destination_node_ids( '%s%d' % (story_domain.NODE_ID_PREFIX, 1), ['%s%d' % (story_domain.NODE_ID_PREFIX, 2)]) story.update_node_exploration_id( '%s%d' % (story_domain.NODE_ID_PREFIX, 1), '0') story.add_node('%s%d' % (story_domain.NODE_ID_PREFIX, 2), 'Dummy Chapter 2') story.update_node_exploration_id( '%s%d' % (story_domain.NODE_ID_PREFIX, 2), '16') skill_services.save_new_skill(self.user_id, skill_1) skill_services.save_new_skill(self.user_id, skill_2) skill_services.save_new_skill(self.user_id, skill_3) story_services.save_new_story(self.user_id, story) topic_services.save_new_topic(self.user_id, topic_1) topic_services.save_new_topic(self.user_id, topic_2) subtopic_page_services.save_subtopic_page( self.user_id, subtopic_page, 'Added subtopic', [ topic_domain.TopicChange({ 'cmd': topic_domain.CMD_ADD_SUBTOPIC, 'subtopic_id': 1, 'title': 'Dummy Subtopic Title' }) ]) topic_services.publish_story(topic_id_1, story_id, self.user_id) topic_services.publish_topic(topic_id_1, self.user_id) else: raise Exception('Cannot load new structures data in production.')
def _load_dummy_new_structures_data(self): """Loads the database with two topics (one of which is empty), a story and three skills in the topic (two of them in a subtopic) and a question attached to each skill. Raises: Exception: Cannot load new structures data in production mode. Exception: User does not have enough rights to generate data. """ if constants.DEV_MODE: if self.user.role != feconf.ROLE_ID_ADMIN: raise Exception( 'User does not have enough rights to generate data.') topic_id_1 = topic_services.get_new_topic_id() topic_id_2 = topic_services.get_new_topic_id() story_id = story_services.get_new_story_id() skill_id_1 = skill_services.get_new_skill_id() skill_id_2 = skill_services.get_new_skill_id() skill_id_3 = skill_services.get_new_skill_id() question_id_1 = question_services.get_new_question_id() question_id_2 = question_services.get_new_question_id() question_id_3 = question_services.get_new_question_id() skill_1 = self._create_dummy_skill(skill_id_1, 'Dummy Skill 1', '<p>Dummy Explanation 1</p>') skill_2 = self._create_dummy_skill(skill_id_2, 'Dummy Skill 2', '<p>Dummy Explanation 2</p>') skill_3 = self._create_dummy_skill(skill_id_3, 'Dummy Skill 3', '<p>Dummy Explanation 3</p>') question_1 = self._create_dummy_question(question_id_1, 'Question 1', [skill_id_1]) question_2 = self._create_dummy_question(question_id_2, 'Question 2', [skill_id_2]) question_3 = self._create_dummy_question(question_id_3, 'Question 3', [skill_id_3]) question_services.add_question(self.user_id, question_1) question_services.add_question(self.user_id, question_2) question_services.add_question(self.user_id, question_3) question_services.create_new_question_skill_link( self.user_id, question_id_1, skill_id_1, 0.3) question_services.create_new_question_skill_link( self.user_id, question_id_2, skill_id_2, 0.5) question_services.create_new_question_skill_link( self.user_id, question_id_3, skill_id_3, 0.7) topic_1 = topic_domain.Topic.create_default_topic( topic_id_1, 'Dummy Topic 1', 'abbrev', 'description') topic_2 = topic_domain.Topic.create_default_topic( topic_id_2, 'Empty Topic', 'abbrev', 'description') topic_1.add_canonical_story(story_id) topic_1.add_uncategorized_skill_id(skill_id_1) topic_1.add_uncategorized_skill_id(skill_id_2) topic_1.add_uncategorized_skill_id(skill_id_3) topic_1.add_subtopic(1, 'Dummy Subtopic Title') topic_1.move_skill_id_to_subtopic(None, 1, skill_id_2) topic_1.move_skill_id_to_subtopic(None, 1, skill_id_3) subtopic_page = ( subtopic_page_domain.SubtopicPage.create_default_subtopic_page( 1, topic_id_1)) # These explorations were chosen since they pass the validations # for published stories. self._reload_exploration('15') self._reload_exploration('25') self._reload_exploration('13') story = story_domain.Story.create_default_story( story_id, 'Help Jaime win the Arcade', topic_id_1) story_node_dicts = [{ 'exp_id': '15', 'title': 'What are the place values?', 'description': 'Jaime learns the place value of each digit ' + 'in a big number.' }, { 'exp_id': '25', 'title': 'Finding the value of a number', 'description': 'Jaime understands the value of his ' + 'arcade score.' }, { 'exp_id': '13', 'title': 'Comparing Numbers', 'description': 'Jaime learns if a number is smaller or ' + 'greater than another number.' }] def generate_dummy_story_nodes(node_id, exp_id, title, description): """Generates and connects sequential story nodes. Args: node_id: int. The node id. exp_id: str. The exploration id. title: str. The title of the story node. description: str. The description of the story node. """ story.add_node('%s%d' % (story_domain.NODE_ID_PREFIX, node_id), title) story.update_node_description( '%s%d' % (story_domain.NODE_ID_PREFIX, node_id), description) story.update_node_exploration_id( '%s%d' % (story_domain.NODE_ID_PREFIX, node_id), exp_id) if node_id != len(story_node_dicts): story.update_node_destination_node_ids( '%s%d' % (story_domain.NODE_ID_PREFIX, node_id), ['%s%d' % (story_domain.NODE_ID_PREFIX, node_id + 1)]) exp_services.update_exploration(self.user_id, exp_id, [ exp_domain.ExplorationChange({ 'cmd': exp_domain.CMD_EDIT_EXPLORATION_PROPERTY, 'property_name': 'category', 'new_value': 'Astronomy' }) ], 'Change category') for i, story_node_dict in enumerate(story_node_dicts): generate_dummy_story_nodes(i + 1, **story_node_dict) skill_services.save_new_skill(self.user_id, skill_1) skill_services.save_new_skill(self.user_id, skill_2) skill_services.save_new_skill(self.user_id, skill_3) story_services.save_new_story(self.user_id, story) topic_services.save_new_topic(self.user_id, topic_1) topic_services.save_new_topic(self.user_id, topic_2) subtopic_page_services.save_subtopic_page( self.user_id, subtopic_page, 'Added subtopic', [ topic_domain.TopicChange({ 'cmd': topic_domain.CMD_ADD_SUBTOPIC, 'subtopic_id': 1, 'title': 'Dummy Subtopic Title' }) ]) topic_services.publish_story(topic_id_1, story_id, self.user_id) else: raise Exception('Cannot load new structures data in production.')
def test_get(self): # Check that non-admins or non-topic managers cannot access the # topics and skills dashboard data. skill_id = skill_services.get_new_skill_id() skill_id_2 = skill_services.get_new_skill_id() self.save_new_skill(skill_id, self.admin_id, 'Description') skill_services.publish_skill(skill_id, self.admin_id) self.save_new_skill(skill_id_2, self.admin_id, 'Description 2') self.login(self.NEW_USER_EMAIL) self.get_json( feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL, expected_status_int=401) self.logout() # Check that admins can access the topics and skills dashboard data. self.login(self.ADMIN_EMAIL) json_response = self.get_json( feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL) self.assertEqual(len(json_response['topic_summary_dicts']), 1) self.assertEqual( json_response['topic_summary_dicts'][0]['can_edit_topic'], True) self.assertEqual( json_response['topic_summary_dicts'][0]['id'], self.topic_id) self.assertEqual( len(json_response['untriaged_skill_summary_dicts']), 1) self.assertEqual( len(json_response['mergeable_skill_summary_dicts']), 1) self.assertEqual( json_response['mergeable_skill_summary_dicts'][0]['id'], self.linked_skill_id) self.assertEqual( json_response['untriaged_skill_summary_dicts'][0]['id'], skill_id) self.assertEqual( len(json_response['unpublished_skill_summary_dicts']), 1) self.assertEqual( json_response['unpublished_skill_summary_dicts'][0]['id'], skill_id_2) self.assertEqual( json_response['can_delete_topic'], True) self.assertEqual( json_response['can_create_topic'], True) self.assertEqual( json_response['can_delete_skill'], True) self.assertEqual( json_response['can_create_skill'], True) self.logout() # Check that topic managers can access the topics and skills # dashboard editable topic data. Topic managers should not have # access to any unpublished skills. self.login(self.TOPIC_MANAGER_EMAIL) json_response = self.get_json( feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL) self.assertEqual(len(json_response['topic_summary_dicts']), 1) self.assertEqual( json_response['topic_summary_dicts'][0]['can_edit_topic'], False) self.assertEqual( json_response['topic_summary_dicts'][0]['id'], self.topic_id) self.assertEqual( json_response['topic_summary_dicts'][0]['id'], self.topic_id) self.assertEqual( len(json_response['untriaged_skill_summary_dicts']), 1) self.assertEqual( len(json_response['mergeable_skill_summary_dicts']), 1) self.assertEqual( json_response['mergeable_skill_summary_dicts'][0]['id'], self.linked_skill_id) self.assertEqual( json_response['untriaged_skill_summary_dicts'][0]['id'], skill_id) self.assertEqual( len(json_response['unpublished_skill_summary_dicts']), 0) self.assertEqual( json_response['can_delete_topic'], False) self.assertEqual( json_response['can_create_topic'], False) self.assertEqual( json_response['can_delete_skill'], False) self.assertEqual( json_response['can_create_skill'], False) self.logout()
def setUp(self): """Completes the sign-up process for the various users.""" super(BaseTopicViewerControllerTests, self).setUp() self.signup(self.NEW_USER_EMAIL, self.NEW_USER_USERNAME) self.user_id = self.get_user_id_from_email(self.NEW_USER_EMAIL) self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.set_admins([self.ADMIN_USERNAME]) self.admin = user_services.UserActionsInfo(self.admin_id) self.topic_id = 'topic' self.story_id_1 = 'story_id_1' self.story_id_2 = 'story_id_2' self.topic_id_1 = 'topic1' self.topic_id_2 = 'topic2' self.skill_id_1 = skill_services.get_new_skill_id() self.skill_id_2 = skill_services.get_new_skill_id() self.story_1 = story_domain.Story.create_default_story( self.story_id_1, 'story_title', self.topic_id_1) self.story_1.description = 'story_description' self.story_1.node_titles = [] self.story_2 = story_domain.Story.create_default_story( self.story_id_2, 'story_title', self.topic_id_2) self.story_2.description = 'story_description' self.story_2.node_titles = [] self.topic = topic_domain.Topic.create_default_topic( self.topic_id, 'public_topic_name', 'abbrev') self.topic.uncategorized_skill_ids.append(self.skill_id_1) self.topic.subtopics.append( topic_domain.Subtopic( 1, 'subtopic_name', [self.skill_id_2], 'image.svg', constants.ALLOWED_THUMBNAIL_BG_COLORS['subtopic'][0])) self.topic.next_subtopic_id = 2 self.topic.thumbnail_filename = 'Image.svg' self.topic.thumbnail_bg_color = ( constants.ALLOWED_THUMBNAIL_BG_COLORS['topic'][0]) self.topic.canonical_story_references.append( topic_domain.StoryReference.create_default_story_reference( self.story_id_1)) self.topic.additional_story_references.append( topic_domain.StoryReference.create_default_story_reference( self.story_id_2)) topic_services.save_new_topic(self.admin_id, self.topic) story_services.save_new_story(self.admin_id, self.story_1) story_services.save_new_story(self.admin_id, self.story_2) self.topic = topic_domain.Topic.create_default_topic( self.topic_id_1, 'private_topic_name', 'abbrev') self.topic.thumbnail_filename = 'Image.svg' self.topic.thumbnail_bg_color = ( constants.ALLOWED_THUMBNAIL_BG_COLORS['topic'][0]) topic_services.save_new_topic(self.admin_id, self.topic) topic_services.publish_topic(self.topic_id, self.admin_id) topic_services.publish_story(self.topic_id, self.story_id_1, self.admin_id) topic_services.publish_story(self.topic_id, self.story_id_2, self.admin_id) self.save_new_skill(self.skill_id_1, self.user_id, description='Skill Description 1') self.save_new_skill(self.skill_id_2, self.user_id, description='Skill Description 2') skill_services.create_user_skill_mastery(self.user_id, self.skill_id_1, 0.3) skill_services.create_user_skill_mastery(self.user_id, self.skill_id_2, 0.5)
def setUp(self): """Before each individual test, create a dummy skill.""" super(ConceptCardDataHandlerTest, self).setUp() self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.set_admins([self.ADMIN_USERNAME]) example_1 = skill_domain.WorkedExample( state_domain.SubtitledHtml('2', '<p>Example Question 1</p>'), state_domain.SubtitledHtml('3', '<p>Example Explanation 1</p>')) example_2 = skill_domain.WorkedExample( state_domain.SubtitledHtml('4', '<p>Example Question 2</p>'), state_domain.SubtitledHtml('5', '<p>Example Explanation 2</p>')) self.skill_contents = skill_domain.SkillContents( state_domain.SubtitledHtml('1', '<p>Skill Explanation</p>'), [example_1, example_2], state_domain.RecordedVoiceovers.from_dict({ 'voiceovers_mapping': { '1': {}, '2': {}, '3': {}, '4': {}, '5': {} } }), state_domain.WrittenTranslations.from_dict({ 'translations_mapping': { '1': {}, '2': {}, '3': {}, '4': {}, '5': {} } })) self.skill_contents_1 = skill_domain.SkillContents( state_domain.SubtitledHtml('1', '<p>Skill Explanation 1</p>'), [example_1, example_2], state_domain.RecordedVoiceovers.from_dict({ 'voiceovers_mapping': { '1': {}, '2': {}, '3': {}, '4': {}, '5': {} } }), state_domain.WrittenTranslations.from_dict({ 'translations_mapping': { '1': {}, '2': {}, '3': {}, '4': {}, '5': {} } })) self.admin = user_services.get_user_actions_info(self.admin_id) self.skill_id = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id, self.admin_id, description='Description', skill_contents=self.skill_contents) self.skill_id_1 = skill_services.get_new_skill_id() self.save_new_skill(self.skill_id_1, self.admin_id, description='Description', skill_contents=self.skill_contents_1) self.skill_id_2 = skill_services.get_new_skill_id()
def test_get_new_skill_id(self): new_skill_id = skill_services.get_new_skill_id() self.assertEqual(len(new_skill_id), 12) self.assertEqual(skill_models.SkillModel.get_by_id(new_skill_id), None)
def test_image_upload_and_download(self): """Test image uploading and downloading.""" self.signup(self.CURRICULUM_ADMIN_EMAIL, self.CURRICULUM_ADMIN_USERNAME) admin_id = self.get_user_id_from_email(self.CURRICULUM_ADMIN_EMAIL) self.set_curriculum_admins([self.CURRICULUM_ADMIN_USERNAME]) subtopic = topic_domain.Subtopic.create_default_subtopic( 1, 'Subtopic Title') story_id = story_services.get_new_story_id() topic_id = topic_fetchers.get_new_topic_id() skill_id = skill_services.get_new_skill_id() self.save_new_story(story_id, admin_id, topic_id) self.save_new_topic(topic_id, admin_id, name='Name', description='Description', canonical_story_ids=[story_id], additional_story_ids=[], uncategorized_skill_ids=[], subtopics=[subtopic], next_subtopic_id=2) self.save_new_skill(skill_id, admin_id, description='Description') # Page context: Exploration. self.login(self.EDITOR_EMAIL) csrf_token = self.get_new_csrf_token() with python_utils.open_file(os.path.join(feconf.TESTS_DATA_DIR, 'img.png'), 'rb', encoding=None) as f: raw_image = f.read() response_dict = self.post_json( '%s/exploration/0' % self.IMAGE_UPLOAD_URL_PREFIX, {'filename': 'test.png'}, csrf_token=csrf_token, upload_files=(('image', 'unused_filename', raw_image), )) filename = response_dict['filename'] self.logout() response = self.get_custom_response( self._get_image_url('exploration', '0', filename), 'image/png') self.assertEqual(response.body, raw_image) # Page context: Topic. self.login(self.CURRICULUM_ADMIN_EMAIL) csrf_token = self.get_new_csrf_token() with python_utils.open_file(os.path.join(feconf.TESTS_DATA_DIR, 'img.png'), 'rb', encoding=None) as f: raw_image = f.read() response_dict = self.post_json( '%s/topic/%s' % (self.IMAGE_UPLOAD_URL_PREFIX, topic_id), {'filename': 'test.png'}, csrf_token=csrf_token, upload_files=(('image', 'unused_filename', raw_image), )) filename = response_dict['filename'] self.logout() response = self.get_custom_response( self._get_image_url('topic', topic_id, filename), 'image/png') self.assertEqual(response.body, raw_image) # Page context: Story. self.login(self.CURRICULUM_ADMIN_EMAIL) csrf_token = self.get_new_csrf_token() with python_utils.open_file(os.path.join(feconf.TESTS_DATA_DIR, 'img.png'), 'rb', encoding=None) as f: raw_image = f.read() response_dict = self.post_json( '%s/story/%s' % (self.IMAGE_UPLOAD_URL_PREFIX, story_id), {'filename': 'test.png'}, csrf_token=csrf_token, upload_files=(('image', 'unused_filename', raw_image), )) filename = response_dict['filename'] self.logout() response = self.get_custom_response( self._get_image_url('story', story_id, filename), 'image/png') self.assertEqual(response.body, raw_image) # Page context: Skill. self.login(self.CURRICULUM_ADMIN_EMAIL) csrf_token = self.get_new_csrf_token() with python_utils.open_file(os.path.join(feconf.TESTS_DATA_DIR, 'img.png'), 'rb', encoding=None) as f: raw_image = f.read() response_dict = self.post_json( '%s/skill/%s' % (self.IMAGE_UPLOAD_URL_PREFIX, skill_id), {'filename': 'test.png'}, csrf_token=csrf_token, upload_files=(('image', 'unused_filename', raw_image), )) filename = response_dict['filename'] self.logout() response = self.get_custom_response( self._get_image_url('skill', skill_id, filename), 'image/png') self.assertEqual(response.body, raw_image) # Image context: Question Suggestions. self.login(self.CURRICULUM_ADMIN_EMAIL) csrf_token = self.get_new_csrf_token() with python_utils.open_file(os.path.join(feconf.TESTS_DATA_DIR, 'img.png'), 'rb', encoding=None) as f: raw_image = f.read() response_dict = self.post_json( '%s/question_suggestions/%s' % (self.IMAGE_UPLOAD_URL_PREFIX, skill_id), {'filename': 'test.png'}, csrf_token=csrf_token, upload_files=(('image', 'unused_filename', raw_image), )) filename = response_dict['filename'] self.logout() response = self.get_custom_response( self._get_image_url('skill', skill_id, filename), 'image/png') self.assertEqual(response.body, raw_image)
def test_get(self): # Check that non-admins or non-topic managers cannot access the # topics and skills dashboard data. skill_id = skill_services.get_new_skill_id() self.save_new_skill(skill_id, self.admin_id, description='Description') self.login(self.NEW_USER_EMAIL) self.get_json(feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL, expected_status_int=401) self.logout() # Check that admins can access the topics and skills dashboard data. self.login(self.CURRICULUM_ADMIN_EMAIL) config_services.set_property(self.admin_id, 'classroom_pages_data', [{ 'url_fragment': 'math', 'name': 'math', 'topic_ids': [self.topic_id], 'topic_list_intro': 'Topics covered', 'course_details': 'Course details' }]) json_response = self.get_json( feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL) self.assertEqual(len(json_response['topic_summary_dicts']), 1) self.assertEqual( json_response['topic_summary_dicts'][0]['can_edit_topic'], True) self.assertEqual(json_response['topic_summary_dicts'][0]['id'], self.topic_id) self.assertEqual(len(json_response['untriaged_skill_summary_dicts']), 1) self.assertEqual(len(json_response['mergeable_skill_summary_dicts']), 2) for skill_dict in json_response['mergeable_skill_summary_dicts']: if skill_dict['description'] == 'Description 3': self.assertEqual(skill_dict['id'], self.linked_skill_id) self.assertEqual(len(json_response['categorized_skills_dict']), 1) self.assertEqual( json_response['untriaged_skill_summary_dicts'][0]['id'], skill_id) self.assertEqual(json_response['can_delete_topic'], True) self.assertEqual(json_response['can_create_topic'], True) self.assertEqual(json_response['can_delete_skill'], True) self.assertEqual(json_response['can_create_skill'], True) self.logout() # Check that topic managers can access the topics and skills # dashboard editable topic data. Topic managers should not have # access to any unpublished skills. self.login(self.TOPIC_MANAGER_EMAIL) json_response = self.get_json( feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL) self.assertEqual(len(json_response['topic_summary_dicts']), 1) self.assertEqual( json_response['topic_summary_dicts'][0]['can_edit_topic'], True) self.assertEqual(json_response['topic_summary_dicts'][0]['id'], self.topic_id) self.assertEqual(json_response['topic_summary_dicts'][0]['id'], self.topic_id) self.assertEqual(len(json_response['untriaged_skill_summary_dicts']), 1) self.assertEqual(len(json_response['mergeable_skill_summary_dicts']), 2) for skill_dict in json_response['mergeable_skill_summary_dicts']: if skill_dict['description'] == 'Description 3': self.assertEqual(skill_dict['id'], self.linked_skill_id) self.assertEqual( json_response['untriaged_skill_summary_dicts'][0]['id'], skill_id) self.assertEqual(len(json_response['all_classroom_names']), 1) self.assertEqual(json_response['all_classroom_names'], ['math']) self.assertEqual(json_response['can_delete_topic'], False) self.assertEqual(json_response['can_create_topic'], False) self.assertEqual(json_response['can_delete_skill'], False) self.assertEqual(json_response['can_create_skill'], False) self.logout()
def test_get(self): self.login(self.CURRICULUM_ADMIN_EMAIL) skill_id = skill_services.get_new_skill_id() self.save_new_skill(skill_id, self.admin_id, description='Skill description') json_response = self.get_json( '%s/%s' % (feconf.UNASSIGN_SKILL_DATA_HANDLER_URL, skill_id)) self.assertEqual(len(json_response['topic_assignment_dicts']), 0) topic_id_1 = topic_fetchers.get_new_topic_id() topic_id_2 = topic_fetchers.get_new_topic_id() self.save_new_topic(topic_id_1, self.admin_id, name='Topic1', abbreviated_name='topic-one', url_fragment='topic-one', description='Description1', canonical_story_ids=[], additional_story_ids=[], uncategorized_skill_ids=[skill_id], subtopics=[], next_subtopic_id=1) subtopic = topic_domain.Subtopic.from_dict({ 'id': 1, 'title': 'subtopic1', 'skill_ids': [skill_id], 'thumbnail_filename': None, 'thumbnail_bg_color': None, 'thumbnail_size_in_bytes': None, 'url_fragment': 'subtopic-url' }) self.save_new_topic(topic_id_2, self.admin_id, name='Topic2', abbreviated_name='topic-two', url_fragment='topic-two', description='Description2', canonical_story_ids=[], additional_story_ids=[], uncategorized_skill_ids=[], subtopics=[subtopic], next_subtopic_id=2) json_response = self.get_json( '%s/%s' % (feconf.UNASSIGN_SKILL_DATA_HANDLER_URL, skill_id)) topic_assignment_dicts = sorted( json_response['topic_assignment_dicts'], key=lambda i: i['topic_name']) self.assertEqual(len(topic_assignment_dicts), 2) self.assertEqual(topic_assignment_dicts[0]['topic_name'], 'Topic1') self.assertEqual(topic_assignment_dicts[0]['topic_id'], topic_id_1) self.assertIsNone(topic_assignment_dicts[0]['subtopic_id']) self.assertEqual(topic_assignment_dicts[1]['topic_name'], 'Topic2') self.assertEqual(topic_assignment_dicts[1]['topic_id'], topic_id_2) self.assertEqual(topic_assignment_dicts[1]['subtopic_id'], 1)
def test_interactions_are_reported_correctly(self): topic_id_1 = 'topicid1' topic_id_2 = 'topicid2' story_id = story_services.get_new_story_id() skill_id_1 = skill_services.get_new_skill_id() skill_id_2 = skill_services.get_new_skill_id() skill_id_3 = skill_services.get_new_skill_id() skill_1 = self._create_dummy_skill(skill_id_1, 'Dummy Skill 1', '<p>Dummy Explanation 1</p>') skill_2 = self._create_dummy_skill(skill_id_2, 'Dummy Skill 2', '<p>Dummy Explanation 2</p>') skill_3 = self._create_dummy_skill(skill_id_3, 'Dummy Skill 3', '<p>Dummy Explanation 3</p>') topic_1 = topic_domain.Topic.create_default_topic( topic_id_1, 'Dummy Topic 1', 'dummy-topic-one', 'description') topic_2 = topic_domain.Topic.create_default_topic( topic_id_2, 'Empty Topic', 'empty-topic', 'description') topic_1.add_canonical_story(story_id) topic_1.add_uncategorized_skill_id(skill_id_1) topic_1.add_uncategorized_skill_id(skill_id_2) topic_1.add_uncategorized_skill_id(skill_id_3) topic_1.add_subtopic(1, 'Dummy Subtopic Title') topic_1.move_skill_id_to_subtopic(None, 1, skill_id_2) topic_1.move_skill_id_to_subtopic(None, 1, skill_id_3) # These explorations were chosen since they pass the validations # for published stories. exp_services.load_demo('15') exp_services.load_demo('25') exp_services.load_demo('13') exp_services.update_exploration( self.user_id, '15', [ exp_domain.ExplorationChange({ 'cmd': exp_domain.CMD_EDIT_EXPLORATION_PROPERTY, 'property_name': 'correctness_feedback_enabled', 'new_value': True }) ], 'Changed correctness_feedback_enabled.') exp_services.update_exploration( self.user_id, '25', [ exp_domain.ExplorationChange({ 'cmd': exp_domain.CMD_EDIT_EXPLORATION_PROPERTY, 'property_name': 'correctness_feedback_enabled', 'new_value': True }) ], 'Changed correctness_feedback_enabled.') exp_services.update_exploration( self.user_id, '13', [ exp_domain.ExplorationChange({ 'cmd': exp_domain.CMD_EDIT_EXPLORATION_PROPERTY, 'property_name': 'correctness_feedback_enabled', 'new_value': True }) ], 'Changed correctness_feedback_enabled.') story = story_domain.Story.create_default_story( story_id, 'Help Jaime win the Arcade', 'Description', topic_id_1, 'help-jamie-win-arcade') story_node_dicts = [{ 'exp_id': '15', 'title': 'What are the place values?', 'description': 'a' }, { 'exp_id': '25', 'title': 'Finding the value of a number', 'description': 'b' }, { 'exp_id': '13', 'title': 'Comparing Numbers', 'description': 'c' }] def generate_dummy_story_nodes(node_id, exp_id, title, description): """Generates and connects sequential story nodes. Args: node_id: int. The node id. exp_id: str. The exploration id. title: str. The title of the story node. description: str. The description of the story node. """ story.add_node('%s%d' % (story_domain.NODE_ID_PREFIX, node_id), title) story.update_node_description( '%s%d' % (story_domain.NODE_ID_PREFIX, node_id), description) story.update_node_exploration_id( '%s%d' % (story_domain.NODE_ID_PREFIX, node_id), exp_id) if node_id != len(story_node_dicts): story.update_node_destination_node_ids( '%s%d' % (story_domain.NODE_ID_PREFIX, node_id), ['%s%d' % (story_domain.NODE_ID_PREFIX, node_id + 1)]) exp_services.update_exploration(self.user_id, exp_id, [ exp_domain.ExplorationChange({ 'cmd': exp_domain.CMD_EDIT_EXPLORATION_PROPERTY, 'property_name': 'category', 'new_value': 'Astronomy' }) ], 'Change category') for i, story_node_dict in enumerate(story_node_dicts): generate_dummy_story_nodes(i + 1, **story_node_dict) skill_services.save_new_skill(self.user_id, skill_1) skill_services.save_new_skill(self.user_id, skill_2) skill_services.save_new_skill(self.user_id, skill_3) story_services.save_new_story(self.user_id, story) topic_services.save_new_topic(self.user_id, topic_1) topic_services.save_new_topic(self.user_id, topic_2) topic_services.publish_story(topic_id_1, story_id, self.user_id) job_id = (topic_jobs_one_off.InteractionsInStoriesAuditOneOffJob. create_new()) topic_jobs_one_off.InteractionsInStoriesAuditOneOffJob.enqueue(job_id) self.process_and_flush_pending_mapreduce_tasks() output = (topic_jobs_one_off.InteractionsInStoriesAuditOneOffJob. get_output(job_id)) expected = [[ '%s (%s)' % ('Dummy Topic 1', topic_id_1), [ u'[u\'EndExploration\', u\'ImageClickInput\', u\'Continue\', ' u'u\'MultipleChoiceInput\', u\'TextInput\']' ] ], ['%s (%s)' % ('Empty Topic', topic_id_2), [u'[]']]] self.assertEqual(expected, [ast.literal_eval(x) for x in output])