Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
 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()
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
    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)
Exemplo n.º 5
0
    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()
Exemplo n.º 6
0
    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})
Exemplo n.º 7
0
    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)
Exemplo n.º 9
0
    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)
Exemplo n.º 11
0
    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})
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
    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()
Exemplo n.º 14
0
    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.')
Exemplo n.º 15
0
    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)
Exemplo n.º 16
0
    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()
Exemplo n.º 17
0
    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
Exemplo n.º 18
0
 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')
Exemplo n.º 19
0
    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()
Exemplo n.º 20
0
    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})
Exemplo n.º 21
0
    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.')
Exemplo n.º 22
0
    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.')
Exemplo n.º 23
0
    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()
Exemplo n.º 24
0
    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)
Exemplo n.º 25
0
    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()
Exemplo n.º 26
0
    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)
Exemplo n.º 27
0
    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)
Exemplo n.º 30
0
    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])