Example #1
0
    def test_get_skill_from_model_with_invalid_misconceptions_schema_version(
            self):
        commit_cmd = skill_domain.SkillChange(
            {'cmd': skill_domain.CMD_CREATE_NEW})
        example_1 = skill_domain.WorkedExample(
            state_domain.SubtitledHtml('2', '<p>Example Question 1</p>'),
            state_domain.SubtitledHtml('3', '<p>Example Explanation 1</p>'))
        model = skill_models.SkillModel(
            id='skill_id',
            description='description',
            language_code='en',
            misconceptions=[],
            rubrics=[],
            next_misconception_id=0,
            misconceptions_schema_version=0,
            rubric_schema_version=3,
            skill_contents_schema_version=2,
            all_questions_merged=False,
            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': {}
                    }})).to_dict())
        commit_cmd_dicts = [commit_cmd.to_dict()]
        model.commit(self.user_id_admin, 'skill model created',
                     commit_cmd_dicts)

        with self.assertRaisesRegex(
                Exception,
                'Sorry, we can only process v1-v%d misconception schemas at '
                'present.' % feconf.CURRENT_MISCONCEPTIONS_SCHEMA_VERSION):
            skill_fetchers.get_skill_from_model(model)
Example #2
0
    def test_conversion_to_and_from_dict(self):
        """Test that to_dict and from_dict preserve all data within a
        skill_contents and misconception object.
        """
        example_1 = skill_domain.WorkedExample(
            state_domain.SubtitledHtml('2', '<p>Example Question 1</p>'),
            state_domain.SubtitledHtml('3', '<p>Example Answer 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': {}
                }}))
        skill_contents_dict = skill_contents.to_dict()
        skill_contents_from_dict = skill_domain.SkillContents.from_dict(
            skill_contents_dict)

        misconceptions = skill_domain.Misconception(self.MISCONCEPTION_ID,
                                                    'Tag Name',
                                                    '<p>Description</p>',
                                                    '<p>Feedback</p>', True)
        misconceptions_dict = misconceptions.to_dict()
        misconceptions_from_dict = skill_domain.Misconception.from_dict(
            misconceptions_dict)

        rubric = skill_domain.Rubric(constants.SKILL_DIFFICULTIES[0],
                                     ['<p>Explanation</p>'])
        rubric_dict = rubric.to_dict()
        rubric_from_dict = skill_domain.Rubric.from_dict(rubric_dict)
        self.assertEqual(skill_contents_from_dict.to_dict(),
                         skill_contents_dict)
        self.assertEqual(misconceptions_from_dict.to_dict(),
                         misconceptions_dict)
        self.assertEqual(rubric_from_dict.to_dict(), rubric_dict)
    def test_conversion_to_and_from_dict(self):
        """Test that to_dict and from_dict preserve all data within a
        skill_contents and misconception object.
        """
        skill_contents = skill_domain.SkillContents(
            state_domain.SubtitledHtml('1', 'Explanation'), [
                state_domain.SubtitledHtml('2', 'Example 1')], {})
        skill_contents_dict = skill_contents.to_dict()
        skill_contents_from_dict = skill_domain.SkillContents.from_dict(
            skill_contents_dict)

        misconceptions = skill_domain.Misconception(
            self.MISCONCEPTION_ID, 'Tag Name', 'Description', 'Feedback')
        misconceptions_dict = misconceptions.to_dict()
        misconceptions_from_dict = skill_domain.Misconception.from_dict(
            misconceptions_dict)
        self.assertEqual(
            skill_contents_from_dict.to_dict(), skill_contents_dict)
        self.assertEqual(
            misconceptions_from_dict.to_dict(), misconceptions_dict)
Example #4
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')
            ], {})
        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)
Example #5
0
    def test_get_suggestions_after_updating_suggestion_summary(self):
        self.login(self.EDITOR_EMAIL)

        response_dict = self.get_json(
            '%s/%s' % (feconf.FEEDBACK_THREADLIST_URL_PREFIX, self.EXP_ID_1))
        thread_id = response_dict['feedback_thread_dicts'][0]['thread_id']
        thread_url = '%s/%s' % (
            feconf.LEARNER_DASHBOARD_FEEDBACK_THREAD_DATA_URL, thread_id)
        response_dict = self.get_json(thread_url)
        messages_summary = response_dict['message_summary_list'][0]

        self.assertEqual(messages_summary['author_username'],
                         self.EDITOR_USERNAME)
        self.assertTrue(messages_summary['author_picture_data_url'].startswith(
            'data:image/png;'))
        self.assertFalse(messages_summary.get('suggestion_html'))
        self.assertFalse(messages_summary.get('current_content_html'))
        self.assertFalse(messages_summary.get('description'))

        new_content = state_domain.SubtitledHtml('content',
                                                 'new content html').to_dict()
        change_cmd = {
            'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
            'property_name': exp_domain.STATE_PROPERTY_CONTENT,
            'state_name': 'Welcome!',
            'new_value': new_content
        }

        suggestion_models.GeneralSuggestionModel.create(
            suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT,
            suggestion_models.TARGET_TYPE_EXPLORATION, self.EXP_ID_1, 1,
            suggestion_models.STATUS_IN_REVIEW, self.editor_id, None,
            change_cmd, 'score category', thread_id)

        suggestion_thread = feedback_services.get_thread(thread_id)
        suggestion = suggestion_services.get_suggestion_by_id(thread_id)
        exploration = exp_services.get_exploration_by_id(self.EXP_ID_1)
        current_content_html = (
            exploration.states[suggestion.change.state_name].content.html)
        response_dict = self.get_json(thread_url)
        messages_summary = response_dict['message_summary_list'][0]

        self.assertEqual(messages_summary['author_username'],
                         self.EDITOR_USERNAME)
        self.assertTrue(messages_summary['author_picture_data_url'].startswith(
            'data:image/png;'))
        self.assertEqual(messages_summary['suggestion_html'],
                         'new content html')
        self.assertEqual(messages_summary['current_content_html'],
                         current_content_html)
        self.assertEqual(messages_summary['description'],
                         suggestion_thread.subject)
        self.logout()
Example #6
0
 def test_fail_to_add_unpublished_skill_id(self):
     self.save_new_skill('skill_a',
                         self.user_id_a,
                         'Description A',
                         misconceptions=[],
                         skill_contents=skill_domain.SkillContents(
                             state_domain.SubtitledHtml('1', 'Explanation'),
                             [state_domain.SubtitledHtml('2', 'Example 1')],
                             {
                                 '1': {},
                                 '2': {}
                             },
                             state_domain.WrittenTranslations.from_dict({
                                 'translations_mapping': {
                                     '1': {},
                                     '2': {}
                                 }
                             })))
     with self.assertRaisesRegexp(
             Exception, 'Cannot assign unpublished skills to a topic'):
         self.topic.add_uncategorized_skill_id('skill_a')
Example #7
0
    def test_skill_contents_audio_validation(self):
        self.skill.update_worked_examples([{
            'content_id': 'content_id_1',
            'html': '<p>Hello</p>'
        }, {
            'content_id': 'content_id_2',
            'html': '<p>Hello 2</p>'
        }])
        self.skill.skill_contents.content_ids_to_audio_translations = {
            'content_id_3': {}
        }
        self._assert_validation_error(
            'Expected content_ids_to_audio_translations to contain only '
            'content_ids in worked examples and explanation.')

        self.skill.skill_contents.worked_examples = [
            state_domain.SubtitledHtml('content_id_1', '<p>Hello</p>'),
            state_domain.SubtitledHtml('content_id_1', '<p>Hello 2</p>')
        ]

        self._assert_validation_error('Found a duplicate content id')
Example #8
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)
    def test_skill_creation_with_valid_images(self):
        self.login(self.CURRICULUM_ADMIN_EMAIL)
        csrf_token = self.get_new_csrf_token()
        filename = 'img.png'
        filename_2 = 'img_2.png'
        explanation_html = (
            '<oppia-noninteractive-image filepath-with-value='
            '"&quot;img.png&quot;" caption-with-value="&quot;&quot;" '
            'alt-with-value="&quot;Image&quot;"></oppia-noninteractive-image>')
        explanation_html_2 = (
            '<oppia-noninteractive-image filepath-with-value='
            '"&quot;img_2.png&quot;" caption-with-value="&quot;&quot;" '
            'alt-with-value="&quot;Image 2&quot;"></oppia-noninteractive-image>'
        )
        rubrics = [{
            'difficulty': constants.SKILL_DIFFICULTIES[0],
            'explanations': ['Explanation 1', explanation_html_2]
        }, {
            'difficulty': constants.SKILL_DIFFICULTIES[1],
            'explanations': ['Explanation 2']
        }, {
            'difficulty': constants.SKILL_DIFFICULTIES[2],
            'explanations': ['Explanation 3']
        }]
        post_data = {
            'description':
            'Skill Description',
            'rubrics':
            rubrics,
            'explanation_dict':
            state_domain.SubtitledHtml('1', explanation_html).to_dict(),
            'thumbnail_filename':
            'image.svg'
        }

        with python_utils.open_file(os.path.join(feconf.TESTS_DATA_DIR,
                                                 'img.png'),
                                    'rb',
                                    encoding=None) as f:
            raw_image = f.read()

        json_response = self.post_json(self.url,
                                       post_data,
                                       csrf_token=csrf_token,
                                       upload_files=(
                                           (filename, filename, raw_image),
                                           (filename_2, filename_2, raw_image),
                                       ))
        skill_id = json_response['skillId']
        self.assertIsNotNone(
            skill_fetchers.get_skill_by_id(skill_id, strict=False))
        self.logout()
    def test_content_ids_to_audio_translations_validation(self):
        self.subtopic_page_contents.content_ids_to_audio_translations = 1
        self._assert_validation_error(
            'Expected content_ids_to_audio_translations to be a dict')

        self.subtopic_page_contents.subtitled_html = (
            state_domain.SubtitledHtml('content', '<p>Test</p>'))
        self.subtopic_page_contents.content_ids_to_audio_translations = {
            'content_id_3': {}
        }
        self._assert_validation_error(
            'Expected content_ids_to_audio_translations to contain '
            'only content_ids in the subtopic page.')
Example #11
0
    def test_get_skill_descriptions_by_ids(self):
        self.save_new_skill('skill_2',
                            self.USER_ID,
                            'Description 2',
                            misconceptions=[],
                            skill_contents=skill_domain.SkillContents(
                                state_domain.SubtitledHtml('1', 'Explanation'),
                                [state_domain.SubtitledHtml('2', 'Example 1')],
                                {}))
        self.save_new_skill('skill_3',
                            self.USER_ID,
                            'Description 3',
                            misconceptions=[],
                            skill_contents=skill_domain.SkillContents(
                                state_domain.SubtitledHtml('1', 'Explanation'),
                                [state_domain.SubtitledHtml('2', 'Example 1')],
                                {}))
        with self.swap(feconf, 'CAN_SEND_EMAILS', True):
            skill_descriptions = skill_services.get_skill_descriptions_by_ids(
                'topic_id', [self.SKILL_ID, 'skill_2', 'skill_3'])
            messages = self.mail_stub.get_sent_messages(
                to=feconf.ADMIN_EMAIL_ADDRESS)
            self.assertEqual(len(messages), 0)

            skill_services.delete_skill(self.USER_ID, 'skill_2')
            skill_descriptions = skill_services.get_skill_descriptions_by_ids(
                'topic_id', [self.SKILL_ID, 'skill_2', 'skill_3'])
            messages = self.mail_stub.get_sent_messages(
                to=feconf.ADMIN_EMAIL_ADDRESS)
            expected_email_html_body = ('The deleted skills: skill_2 are still'
                                        ' present in topic with id topic_id')
            self.assertEqual(len(messages), 1)
            self.assertIn(expected_email_html_body, messages[0].html.decode())
            self.assertEqual(
                skill_descriptions, {
                    self.SKILL_ID: 'Description',
                    'skill_2': None,
                    'skill_3': 'Description 3'
                })
Example #12
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'])
Example #13
0
 def setUp(self):
     super(SkillDomainUnitTests, self).setUp()
     skill_contents = skill_domain.SkillContents(
         state_domain.SubtitledHtml('1', 'Explanation'),
         [state_domain.SubtitledHtml('2', 'Example 1')], {
             '1': {},
             '2': {}
         },
         state_domain.WrittenTranslations.from_dict(
             {'translations_mapping': {
                 '1': {},
                 '2': {}
             }}))
     misconceptions = [
         skill_domain.Misconception(self.MISCONCEPTION_ID, 'name', 'notes',
                                    'default_feedback')
     ]
     self.skill = skill_domain.Skill(
         self.SKILL_ID, 'Description', misconceptions, skill_contents,
         feconf.CURRENT_MISCONCEPTIONS_SCHEMA_VERSION,
         feconf.CURRENT_SKILL_CONTENTS_SCHEMA_VERSION, 'en', 0, 1, None,
         False)
Example #14
0
 def setUp(self):
     super(SkillDomainUnitTests, 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, 'name',
                                    '<p>notes</p>',
                                    '<p>default_feedback</p>', True)
     ]
     rubrics = [
         skill_domain.Rubric(constants.SKILL_DIFFICULTIES[0],
                             ['<p>Explanation 1</p>']),
         skill_domain.Rubric(constants.SKILL_DIFFICULTIES[1],
                             ['<p>Explanation 2</p>']),
         skill_domain.Rubric(constants.SKILL_DIFFICULTIES[2],
                             ['<p>Explanation 3</p>'])
     ]
     self.skill = skill_domain.Skill(
         self.SKILL_ID, 'Description', misconceptions, rubrics,
         skill_contents, feconf.CURRENT_MISCONCEPTIONS_SCHEMA_VERSION,
         feconf.CURRENT_RUBRIC_SCHEMA_VERSION,
         feconf.CURRENT_SKILL_CONTENTS_SCHEMA_VERSION, 'en', 0, 1, None,
         False, ['skill_id_2'])
Example #15
0
    def test_conversion_to_and_from_dict(self):
        """Test that to_dict and from_dict preserve all data within a
        skill_contents and misconception object.
        """
        audio_translation = {
            'en':
            state_domain.AudioTranslation.from_dict({
                'filename': 'file.mp3',
                'file_size_bytes': 'size',
                'needs_update': True
            })
        }
        skill_contents = skill_domain.SkillContents(
            state_domain.SubtitledHtml('1', 'Explanation'),
            [state_domain.SubtitledHtml('2', 'Example 1')], {
                '1': audio_translation,
                '2': {}
            },
            state_domain.WrittenTranslations.from_dict(
                {'translations_mapping': {
                    '1': {},
                    '2': {}
                }}))
        skill_contents_dict = skill_contents.to_dict()
        skill_contents_from_dict = skill_domain.SkillContents.from_dict(
            skill_contents_dict)

        misconceptions = skill_domain.Misconception(self.MISCONCEPTION_ID,
                                                    'Tag Name', 'Description',
                                                    'Feedback')
        misconceptions_dict = misconceptions.to_dict()
        misconceptions_from_dict = skill_domain.Misconception.from_dict(
            misconceptions_dict)
        self.assertEqual(skill_contents_from_dict.to_dict(),
                         skill_contents_dict)
        self.assertEqual(misconceptions_from_dict.to_dict(),
                         misconceptions_dict)
    def test_get_multi_skills(self):
        self.save_new_skill(
            'skill_a', self.user_id_admin, description='Description A',
            misconceptions=[],
            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': {}}})))
        self.save_new_skill(
            'skill_b', self.user_id_admin, description='Description B',
            misconceptions=[],
            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': {}}})))

        skills = skill_services.get_multi_skills(['skill_a', 'skill_b'])

        self.assertEqual(len(skills), 2)

        self.assertEqual(skills[0].id, 'skill_a')
        self.assertEqual(skills[0].description, 'Description A')
        self.assertEqual(skills[0].misconceptions, [])

        self.assertEqual(skills[1].id, 'skill_b')
        self.assertEqual(skills[1].description, 'Description B')
        self.assertEqual(skills[1].misconceptions, [])

        with self.assertRaisesRegexp(
            Exception, 'No skill exists for ID skill_c'):
            skill_services.get_multi_skills(['skill_a', 'skill_c'])
Example #17
0
    def test_cron_accept_stale_suggestions_handler(self):
        self.login(self.ADMIN_EMAIL, is_super_admin=True)
        self.save_new_valid_exploration('exp_id',
                                        self.admin_id,
                                        title='A title',
                                        category='Algebra')
        author_id = self.get_user_id_from_email(self.ADMIN_EMAIL)

        new_content = state_domain.SubtitledHtml(
            'content', '<p>new suggestion content</p>').to_dict()
        change = {
            'cmd': 'edit_state_property',
            'property_name': 'content',
            'state_name': 'Introduction',
            'new_value': new_content
        }
        suggestion_services.create_suggestion(
            suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT,
            suggestion_models.TARGET_TYPE_EXPLORATION, 'exp_id', 1, author_id,
            change, 'change title')

        exploration = exp_fetchers.get_exploration_by_id('exp_id')
        self.assertEqual(exploration.states['Introduction'].content.to_dict(),
                         {
                             'content_id': 'content',
                             'html': ''
                         })

        threshold_time_before_accept_swap = self.swap(
            suggestion_models, 'THRESHOLD_TIME_BEFORE_ACCEPT_IN_MSECS', 0)
        auto_accept_suggestions_swap = self.swap(
            feconf, 'ENABLE_AUTO_ACCEPT_OF_SUGGESTIONS', True)

        with threshold_time_before_accept_swap, self.testapp_swap, (
                auto_accept_suggestions_swap):
            self.assertEqual(
                len(suggestion_services.get_all_stale_suggestions()), 1)
            self.get_html_response(
                '/cron/suggestions/accept_stale_suggestions')

            self.assertEqual(
                len(suggestion_services.get_all_stale_suggestions()), 0)

        exploration = exp_fetchers.get_exploration_by_id('exp_id')
        self.assertEqual(exploration.states['Introduction'].content.to_dict(),
                         {
                             'content_id': 'content',
                             'html': '<p>new suggestion content</p>'
                         })
    def setUp(self):
        """Before each individual test, create a dummy skill."""
        super(ConceptCardDataHandlerTest, self).setUp()
        self.signup(self.CURRICULUM_ADMIN_EMAIL, self.CURRICULUM_ADMIN_USERNAME)

        self.admin_id = self.get_user_id_from_email(self.CURRICULUM_ADMIN_EMAIL)

        self.set_curriculum_admins([self.CURRICULUM_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_subtitled_html_validation(self):
        """Test validation of subtitled HTML."""
        subtitled_html = state_domain.SubtitledHtml('content_id', 'some html')
        subtitled_html.validate()

        with self.assertRaisesRegexp(utils.ValidationError,
                                     'Invalid content HTML'):
            with self.swap(subtitled_html, 'html', 20):
                subtitled_html.validate()

        with self.assertRaisesRegexp(
                utils.ValidationError,
                'Expected content id to be a string, ' + 'received 20'):
            with self.swap(subtitled_html, 'content_id', 20):
                subtitled_html.validate()
Example #20
0
 def test_skill_creation_in_invalid_topic(self):
     self.login(self.ADMIN_EMAIL)
     csrf_token = self.get_new_csrf_token()
     payload = {
         'description': 'Skill Description',
         'linked_topic_ids': ['topic'],
         'rubrics': [],
         'explanation_dict': state_domain.SubtitledHtml(
             '1', '<p>Explanation</p>').to_dict()
     }
     json_response = self.post_json(
         self.url, payload, csrf_token=csrf_token,
         expected_status_int=400)
     self.assertEqual(json_response['status_code'], 400)
     self.logout()
Example #21
0
    def test_skill_creation_with_invalid_images(self):
        self.login(self.ADMIN_EMAIL)
        csrf_token = self.get_new_csrf_token()
        explanation_html = (
            '<oppia-noninteractive-image filepath-with-value='
            '"&quot;img.svg&quot;" caption-with-value="&quot;&quot;" '
            'alt-with-value="&quot;Image&quot;"></oppia-noninteractive-image>')
        rubrics = [{
            'difficulty': constants.SKILL_DIFFICULTIES[0],
            'explanations': ['Explanation 1']
        }, {
            'difficulty': constants.SKILL_DIFFICULTIES[1],
            'explanations': ['Explanation 2']
        }, {
            'difficulty': constants.SKILL_DIFFICULTIES[2],
            'explanations': ['Explanation 3']
        }]
        post_data = {
            'description':
            'Skill Description',
            'rubrics':
            rubrics,
            'explanation_dict':
            state_domain.SubtitledHtml('1', explanation_html).to_dict(),
            'thumbnail_filename':
            'image.svg'
        }

        response_dict = self.post_json(self.url,
                                       post_data,
                                       csrf_token=csrf_token,
                                       expected_status_int=400)

        self.assertIn('No image data provided for file with name img.svg',
                      response_dict['error'])

        large_image = '<svg><path d="%s" /></svg>' % (
            'M150 0 L75 200 L225 200 Z ' * 4000)
        response_dict = self.post_json(self.url,
                                       post_data,
                                       csrf_token=csrf_token,
                                       upload_files=(('img.svg', 'img.svg',
                                                      large_image), ),
                                       expected_status_int=400)

        self.assertIn('Image exceeds file size limit of 100 KB.',
                      response_dict['error'])
        self.logout()
    def test_migrate_misconceptions_to_latest_schema(self):
        commit_cmd = skill_domain.SkillChange({
            'cmd': skill_domain.CMD_CREATE_NEW
        })
        explanation_content_id = feconf.DEFAULT_SKILL_EXPLANATION_CONTENT_ID
        skill_contents = skill_domain.SkillContents(
            state_domain.SubtitledHtml(
                explanation_content_id, feconf.DEFAULT_SKILL_EXPLANATION), [],
            state_domain.RecordedVoiceovers.from_dict({
                'voiceovers_mapping': {
                    explanation_content_id: {}
                }
            }),
            state_domain.WrittenTranslations.from_dict({
                'translations_mapping': {
                    explanation_content_id: {}
                }
            }))
        model = skill_models.SkillModel(
            id='skill_id',
            description='description',
            language_code='en',
            misconceptions=[{
                'id': 1,
                'name': 'name',
                'notes': 'notes',
                'feedback': 'default_feedback'
            }],
            rubrics=[],
            skill_contents=skill_contents.to_dict(),
            next_misconception_id=2,
            misconceptions_schema_version=1,
            rubric_schema_version=1,
            skill_contents_schema_version=1,
            all_questions_merged=False
        )
        commit_cmd_dicts = [commit_cmd.to_dict()]
        model.commit(
            'user_id_admin', 'skill model created', commit_cmd_dicts)

        current_schema_version_swap = self.swap(
            feconf, 'CURRENT_MISCONCEPTIONS_SCHEMA_VERSION', 2)

        with current_schema_version_swap:
            skill = skill_services.get_skill_from_model(model)

        self.assertEqual(skill.misconceptions_schema_version, 2)
        self.assertEqual(skill.misconceptions[0].must_be_addressed, True)
Example #23
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', '<p>Skill Explanation</p>'), [
                state_domain.SubtitledHtml('2', '<p>Example 1</p>'),
                state_domain.SubtitledHtml('3', '<p>Example 2</p>')
            ],
            state_domain.RecordedVoiceovers.from_dict(
                {'voiceovers_mapping': {
                    '1': {},
                    '2': {},
                    '3': {}
                }}),
            state_domain.WrittenTranslations.from_dict(
                {'translations_mapping': {
                    '1': {},
                    '2': {},
                    '3': {}
                }}))
        self.skill_contents_1 = skill_domain.SkillContents(
            state_domain.SubtitledHtml('1', '<p>Skill Explanation 1</p>'), [
                state_domain.SubtitledHtml('2', '<p>Example 3</p>'),
                state_domain.SubtitledHtml('3', '<p>Example 4</p>')
            ],
            state_domain.RecordedVoiceovers.from_dict(
                {'voiceovers_mapping': {
                    '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)
        self.skill_id_1 = skill_services.get_new_skill_id()
        self.save_new_skill(self.skill_id_1,
                            self.admin_id,
                            'Description',
                            skill_contents=self.skill_contents_1)
        self.skill_id_2 = skill_services.get_new_skill_id()
Example #24
0
    def test_no_action_is_performed_for_deleted_exploration(self):
        """Test that no action is performed on deleted explorations."""

        exploration = exp_domain.Exploration.create_default_exploration(
            self.VALID_EXP_ID, title='title', category='category')

        exploration.add_states(['State1'])

        state1 = exploration.states['State1']

        state1.update_interaction_id('MultipleChoiceInput')

        customization_args_dict = {
            'choices': {'value': [{
                'html': '<p>This is value1 for MultipleChoiceInput</p>',
                'content_id': 'ca_choices_0'
            }, {
                'html': '<p>This is value2 for MultipleChoiceInput</p>',
                'content_id': 'ca_choices_1'
            }]},
            'showChoicesInShuffledOrder': {'value': True}
        }

        state_answer_group_list = [state_domain.AnswerGroup(
            state_domain.Outcome(
                'State1', state_domain.SubtitledHtml(
                    'feedback', '<p>Outcome for state2</p>'),
                False, [], None, None),
            [
                state_domain.RuleSpec('Equals', {'x': '0'}),
                state_domain.RuleSpec(
                    'Equals', {'x': '9007199254740991'})
            ],
            [],
            None
        )]

        state1.update_interaction_customization_args(customization_args_dict)
        state1.update_next_content_id_index(2)
        state1.update_interaction_answer_groups(state_answer_group_list)

        exp_services.save_new_exploration(self.albert_id, exploration)

        exp_services.delete_exploration(self.albert_id, self.VALID_EXP_ID)

        run_job_for_deleted_exp(
            self, interaction_jobs_one_off.MultipleChoiceInteractionOneOffJob)
Example #25
0
    def test_migrate_rubrics_to_latest_schema(self):
        skill_services.create_new_skill_rights('skill_id', 'user_id_admin')
        commit_cmd = skill_domain.SkillChange({
            'cmd': skill_domain.CMD_CREATE_NEW
        })
        explanation_content_id = feconf.DEFAULT_SKILL_EXPLANATION_CONTENT_ID
        skill_contents = skill_domain.SkillContents(
            state_domain.SubtitledHtml(
                explanation_content_id, feconf.DEFAULT_SKILL_EXPLANATION), [],
            state_domain.RecordedVoiceovers.from_dict({
                'voiceovers_mapping': {
                    explanation_content_id: {}
                }
            }),
            state_domain.WrittenTranslations.from_dict({
                'translations_mapping': {
                    explanation_content_id: {}
                }
            }))
        rubric = skill_domain.Rubric(
            constants.SKILL_DIFFICULTIES[0], '<p>Explanation</p>')
        model = skill_models.SkillModel(
            id='skill_id',
            description='description',
            language_code='en',
            misconceptions=[],
            rubrics=[rubric.to_dict()],
            skill_contents=skill_contents.to_dict(),
            next_misconception_id=1,
            misconceptions_schema_version=1,
            rubric_schema_version=1,
            skill_contents_schema_version=1,
            all_questions_merged=False
        )
        commit_cmd_dicts = [commit_cmd.to_dict()]
        model.commit(
            'user_id_admin', 'skill model created', commit_cmd_dicts)

        swap_skill_object = self.swap(skill_domain, 'Skill', MockSkillObject)
        current_schema_version_swap = self.swap(
            feconf, 'CURRENT_RUBRIC_SCHEMA_VERSION', 2)

        with swap_skill_object, current_schema_version_swap:
            skill = skill_services.get_skill_from_model(model)

        self.assertEqual(skill.rubric_schema_version, 2)
    def setUp(self):
        super(SuggestionMigrationOneOffJobTest, self).setUp()

        self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME)
        self.signup(self.EDITOR_EMAIL, self.EDITOR_USERNAME)
        self.signup(self.AUTHOR_EMAIL, 'author')
        self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL)
        self.editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)
        self.author_id = self.get_user_id_from_email(self.AUTHOR_EMAIL)

        self.editor = user_services.UserActionsInfo(self.editor_id)

        # Login and create exploration and suggestions.
        self.login(self.EDITOR_EMAIL)

        # Create exploration.
        self.save_new_valid_exploration(self.EXP_ID,
                                        self.editor_id,
                                        title='Exploration for suggestions',
                                        category='Algebra',
                                        objective='Test a suggestion.')

        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        init_state = exploration.states[exploration.init_state_name]
        init_interaction = init_state.interaction
        init_interaction.default_outcome.dest = exploration.init_state_name
        self.old_content = state_domain.SubtitledHtml('content',
                                                      'old content').to_dict()
        exp_services.update_exploration(self.editor_id, self.EXP_ID, [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_ADD_STATE,
                'state_name': 'State 1',
            }),
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'state_name': 'State 1',
                'property_name': exp_domain.STATE_PROPERTY_CONTENT,
                'new_value': self.old_content
            })
        ], 'Add state name')

        rights_manager.publish_exploration(self.editor, self.EXP_ID)
        rights_manager.assign_role_for_exploration(self.editor, self.EXP_ID,
                                                   self.owner_id,
                                                   rights_manager.ROLE_EDITOR)
Example #27
0
    def test_feedback_threads_with_suggestions(self):
        with self.swap(constants, 'ENABLE_GENERALIZED_FEEDBACK_THREADS', False):
            new_content = state_domain.SubtitledHtml(
                'content', 'new content html').to_dict()
            change_cmd = {
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name': exp_domain.STATE_PROPERTY_CONTENT,
                'state_name': 'State 1',
                'new_value': new_content
            }
            suggestion_services.create_suggestion(
                suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT,
                suggestion_models.TARGET_TYPE_EXPLORATION, self.EXP_ID, 1,
                self.user_id, change_cmd, 'sample description', None)
            response = self.get_json(
                '%s/%s' % (
                    feconf.FEEDBACK_THREADLIST_URL_PREFIX, self.EXP_ID))
            self.assertEqual(response['feedback_thread_dicts'], [])
            expected_thread_dict = {
                'original_author_username': self.USER_USERNAME,
                'status': feedback_models.STATUS_CHOICES_OPEN,
                'subject': 'sample description'
            }
            self.assertDictContainsSubset(
                expected_thread_dict,
                response['suggestion_thread_dicts'][0])

            thread_id = (
                response['suggestion_thread_dicts'][0]['thread_id'])

            response = self.get_json(
                '%s/%s' % (
                    feconf.FEEDBACK_THREAD_URL_PREFIX, thread_id))
            expected_suggestion_dict = {
                'suggestion_type': (
                    suggestion_models
                    .SUGGESTION_TYPE_EDIT_STATE_CONTENT),
                'target_type': (
                    suggestion_models.TARGET_TYPE_EXPLORATION),
                'target_id': self.EXP_ID,
                'status': suggestion_models.STATUS_IN_REVIEW,
                'author_name': self.USER_USERNAME
            }
            self.assertDictContainsSubset(
                expected_suggestion_dict, response['suggestion'])
 def test_skill_creation_in_invalid_topic(self):
     self.login(self.CURRICULUM_ADMIN_EMAIL)
     csrf_token = self.get_new_csrf_token()
     payload = {
         'description': 'Skill Description',
         'linked_topic_ids': ['topic'],
         'rubrics': [],
         'explanation_dict': state_domain.SubtitledHtml(
             '1', '<p>Explanation</p>').to_dict(),
         'thumbnail_filename': 'image.svg'
     }
     json_response = self.post_json(
         self.url, payload, csrf_token=csrf_token,
         expected_status_int=400,
         upload_files=((
             'image', 'unused_filename', self.original_image_content),))
     self.assertEqual(json_response['status_code'], 400)
     self.logout()
    def test_solution_validation(self):
        """Test validation of state solution."""
        exploration = exp_domain.Exploration.create_default_exploration('eid')
        exploration.objective = 'Objective'
        init_state = exploration.states[exploration.init_state_name]
        init_state.update_interaction_id('TextInput')
        exploration.validate()

        # Solution should be set to None as default.
        self.assertEqual(init_state.interaction.solution, None)

        init_state.add_hint(state_domain.SubtitledHtml('hint_1', {}))
        solution = {
            'answer_is_exclusive': False,
            'correct_answer': [0, 0],
            'explanation': {
                'content_id': 'solution',
                'html': 'hello_world is a string'
            }
        }

        # Object type of answer must match that of correct_answer.
        with self.assertRaises(AssertionError):
            init_state.interaction.solution = (state_domain.Solution.from_dict(
                init_state.interaction.id, solution))

        solution = {
            'answer_is_exclusive': False,
            'correct_answer': 'hello_world!',
            'explanation': {
                'content_id': 'solution',
                'html': 'hello_world is a string'
            }
        }
        init_state.interaction.solution = (state_domain.Solution.from_dict(
            init_state.interaction.id, solution))
        init_state.update_content_ids_to_audio_translations({
            'content': {},
            'default_outcome': {},
            'hint_1': {},
            'solution': {}
        })
        exploration.validate()
    def test_get_concept_cards(self):
        json_response = self.get_json(
            '%s/%s,%s' % (
                feconf.CONCEPT_CARD_DATA_URL_PREFIX,
                self.skill_id, self.skill_id_1)
            )
        self.assertEqual(2, len(json_response['concept_card_dicts']))
        self.assertEqual(
            '<p>Skill Explanation</p>',
            json_response['concept_card_dicts'][0]['explanation']['html'])
        self.assertEqual(
            [skill_domain.WorkedExample(
                state_domain.SubtitledHtml(
                    '2', '<p>Example Question 1</p>'),
                state_domain.SubtitledHtml(
                    '3', '<p>Example Explanation 1</p>')
            ).to_dict(), skill_domain.WorkedExample(
                state_domain.SubtitledHtml(
                    '4', '<p>Example Question 2</p>'),
                state_domain.SubtitledHtml(
                    '5', '<p>Example Explanation 2</p>')
            ).to_dict()],
            json_response['concept_card_dicts'][0]['worked_examples'])

        self.assertEqual(
            '<p>Skill Explanation 1</p>',
            json_response['concept_card_dicts'][1]['explanation']['html'])
        self.assertEqual(
            [skill_domain.WorkedExample(
                state_domain.SubtitledHtml(
                    '2', '<p>Example Question 1</p>'),
                state_domain.SubtitledHtml(
                    '3', '<p>Example Explanation 1</p>')
            ).to_dict(), skill_domain.WorkedExample(
                state_domain.SubtitledHtml(
                    '4', '<p>Example Question 2</p>'),
                state_domain.SubtitledHtml(
                    '5', '<p>Example Explanation 2</p>')
            ).to_dict()],
            json_response['concept_card_dicts'][1]['worked_examples'])