def test_cannot_get_question_from_model_with_invalid_schema_version(self):
        # Delete all question models.
        all_question_models = question_models.QuestionModel.get_all()
        question_models.QuestionModel.delete_multi(
            [question_model.id for question_model in all_question_models],
            feconf.SYSTEM_COMMITTER_ID, '', force_deletion=True)

        all_question_models = question_models.QuestionModel.get_all()
        self.assertEqual(all_question_models.count(), 0)

        question_id = question_services.get_new_question_id()

        question_model = question_models.QuestionModel(
            id=question_id,
            question_state_data=(
                self._create_valid_question_data('ABC').to_dict()),
            language_code='en',
            version=0,
            question_state_data_schema_version=0)

        question_model.commit(
            self.editor_id, 'question model created',
            [{'cmd': question_domain.CMD_CREATE_NEW}])

        all_question_models = question_models.QuestionModel.get_all()
        self.assertEqual(all_question_models.count(), 1)
        question_model = all_question_models.get()

        with self.assertRaisesRegexp(
            Exception,
            'Sorry, we can only process v25-v%d state schemas at present.' %
            feconf.CURRENT_STATE_SCHEMA_VERSION):
            question_fetchers.get_question_from_model(question_model)
Exemple #2
0
    def test_post_with_valid_images(self):
        """Test question creation with valid images."""
        self.login(self.CURRICULUM_ADMIN_EMAIL)
        csrf_token = self.get_new_csrf_token()
        filename = 'img.png'
        question_dict = self.question.to_dict()
        question_dict['id'] = None
        question_dict['version'] = 0
        content_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>')
        question_dict['question_state_data']['content']['html'] = content_html
        post_data = {
            'question_dict': question_dict,
            'skill_ids': [self.skill_id],
            'skill_difficulties': [0.6]
        }

        with python_utils.open_file(os.path.join(feconf.TESTS_DATA_DIR,
                                                 'img.png'),
                                    'rb',
                                    encoding=None) as f:
            raw_image = f.read()
        self.post_json(feconf.NEW_QUESTION_URL,
                       post_data,
                       csrf_token=csrf_token,
                       upload_files=((filename, filename, raw_image), ))
        all_models = question_models.QuestionModel.get_all()
        questions = [
            question_fetchers.get_question_from_model(model)
            for model in all_models
        ]
        self.assertEqual(len(questions), 2)
        self.logout()
Exemple #3
0
    def map(item):
        if item.deleted:
            yield (QuestionMigrationOneOffJob._DELETED_KEY, 1)
            return

        # Note: the read will bring the question up to the newest version.
        question = question_fetchers.get_question_from_model(item)
        try:
            question.validate()
        except Exception as e:
            logging.exception(
                'Question %s failed validation: %s' % (item.id, e))
            yield (
                QuestionMigrationOneOffJob._ERROR_KEY,
                'Question %s failed validation: %s' % (item.id, e))
            return

        # Write the new question into the datastore if it's different from
        # the old version.
        if (item.question_state_data_schema_version <=
                feconf.CURRENT_STATE_SCHEMA_VERSION):
            commit_cmds = [question_domain.QuestionChange({
                'cmd': question_domain.CMD_MIGRATE_STATE_SCHEMA_TO_LATEST_VERSION, # pylint: disable=line-too-long
                'from_version': item.question_state_data_schema_version,
                'to_version': feconf.CURRENT_STATE_SCHEMA_VERSION
            })]
            question_services.update_question(
                feconf.MIGRATION_BOT_USERNAME, item.id, commit_cmds,
                'Update question state schema version to %d.' % (
                    feconf.CURRENT_STATE_SCHEMA_VERSION))
            yield (QuestionMigrationOneOffJob._MIGRATED_KEY, 1)
Exemple #4
0
    def map(item):
        if item.deleted:
            yield (FixQuestionImagesStorageOneOffJob._DELETED_KEY, 1)
            return

        question = question_fetchers.get_question_from_model(item)
        html_list = question.question_state_data.get_all_html_content_strings()
        image_filenames = html_cleaner.get_image_filenames_from_html_strings(
            html_list)
        file_system_class = fs_services.get_entity_file_system_class()
        question_fs = fs_domain.AbstractFileSystem(file_system_class(
            feconf.ENTITY_TYPE_QUESTION, question.id))
        success_count = 0
        # For each image filename, check if it exists in the correct path. If
        # not, copy the image file to the correct path else continue.
        for image_filename in image_filenames:
            if not question_fs.isfile('image/%s' % image_filename):
                for skill_id in question.linked_skill_ids:
                    skill_fs = fs_domain.AbstractFileSystem(file_system_class(
                        feconf.ENTITY_TYPE_SKILL, skill_id))
                    if skill_fs.isfile('image/%s' % image_filename):
                        fs_services.copy_images(
                            feconf.ENTITY_TYPE_SKILL, skill_id,
                            feconf.ENTITY_TYPE_QUESTION, question.id,
                            [image_filename])
                        success_count += 1
                        break
        if success_count > 0:
            yield (
                FixQuestionImagesStorageOneOffJob._IMAGE_COPIED,
                '%s image paths were fixed for question id %s with '
                'linked_skill_ids: %r' % (
                    success_count, question.id, question.linked_skill_ids))
Exemple #5
0
    def test_get_question_from_model_with_current_valid_schema_version(self):
        # Delete all question models.
        all_question_models = question_models.QuestionModel.get_all()
        question_models.QuestionModel.delete_multi(
            [question_model.id for question_model in all_question_models],
            feconf.SYSTEM_COMMITTER_ID,
            '',
            force_deletion=True)

        all_question_models = question_models.QuestionModel.get_all()
        self.assertEqual(all_question_models.count(), 0)

        question_id = question_services.get_new_question_id()

        question_model = question_models.QuestionModel(
            id=question_id,
            question_state_data=(
                self._create_valid_question_data('ABC').to_dict()),
            language_code='en',
            version=0,
            question_state_data_schema_version=48)

        question_model.commit(self.editor_id, 'question model created',
                              [{
                                  'cmd': question_domain.CMD_CREATE_NEW
                              }])

        all_question_models = question_models.QuestionModel.get_all()
        self.assertEqual(all_question_models.count(), 1)
        question_model = all_question_models.get()
        updated_question_model = question_fetchers.get_question_from_model(
            question_model)
        self.assertEqual(
            updated_question_model.question_state_data_schema_version,
            feconf.CURRENT_STATE_SCHEMA_VERSION)
Exemple #6
0
 def test_post_with_topic_manager_email_allows_question_creation(self):
     self.login(self.TOPIC_MANAGER_EMAIL)
     csrf_token = self.get_new_csrf_token()
     question_dict = self.question.to_dict()
     question_dict['id'] = None
     self.post_json(
         feconf.NEW_QUESTION_URL, {
             'question_dict': question_dict,
             'skill_ids': [self.skill_id],
             'skill_difficulties': [0.6]
         }, csrf_token=csrf_token)
     all_models = question_models.QuestionModel.get_all()
     questions = [
         question_fetchers.get_question_from_model(model)
         for model in all_models
     ]
     self.assertEqual(len(questions), 2)
     self.logout()
Exemple #7
0
def get_question_by_id(question_id, strict=True):
    """Returns a domain object representing a question.

    Args:
        question_id: str. ID of the question.
        strict: bool. Whether to fail noisily if no question with the given
            id exists in the datastore.

    Returns:
        Question or None. The domain object representing a question with the
        given id, or None if it does not exist.
    """
    question_model = question_models.QuestionModel.get(
        question_id, strict=strict)
    if question_model:
        question = question_fetchers.get_question_from_model(question_model)
        return question
    else:
        return None
 def test_post_with_admin_email_allows_question_creation(self):
     self.login(self.ADMIN_EMAIL)
     csrf_token = self.get_new_csrf_token()
     question_dict = self.question.to_dict()
     question_dict['id'] = None
     question_dict['version'] = 0
     self.post_json(
         feconf.NEW_QUESTION_URL, {
             'question_dict': question_dict,
             'skill_ids': [self.skill_id],
             'skill_difficulties': [0.6]
         }, csrf_token=csrf_token, expected_status_int=200)
     all_models = question_models.QuestionModel.get_all()
     questions = [
         question_fetchers.get_question_from_model(model)
         for model in all_models
     ]
     self.assertEqual(len(questions), 2)
     self.logout()
    def test_get_question_from_model_with_current_valid_schema_version(
            self) -> None:
        # Delete all question models.
        all_question_models = question_models.QuestionModel.get_all()
        question_models.QuestionModel.delete_multi(
            [question_model.id for question_model in all_question_models],
            feconf.SYSTEM_COMMITTER_ID,
            '',
            force_deletion=True)

        all_question_models = question_models.QuestionModel.get_all()
        self.assertEqual(all_question_models.count(), 0)

        question_id = question_services.get_new_question_id(
        )  # type: ignore[no-untyped-call]

        question_model = question_models.QuestionModel(
            id=question_id,
            question_state_data=(self._create_valid_question_data(
                'ABC').to_dict()),  # type: ignore[no-untyped-call]
            language_code='en',
            version=0,
            question_state_data_schema_version=48)

        question_model.commit(self.editor_id, 'question model created',
                              [{
                                  'cmd': question_domain.CMD_CREATE_NEW
                              }])

        all_question_models = question_models.QuestionModel.get_all()
        self.assertEqual(all_question_models.count(), 1)
        fetched_question_models = all_question_models.get()
        # Ruling out the possibility of None for mypy type checking.
        assert fetched_question_models is not None
        updated_question_model = question_fetchers.get_question_from_model(
            fetched_question_models)
        self.assertEqual(
            updated_question_model.question_state_data_schema_version,
            feconf.CURRENT_STATE_SCHEMA_VERSION)
Exemple #10
0
    def test_migrate_question_state_from_v29_to_v30(self):
        answer_group = {
            'outcome': {
                'dest': 'abc',
                'feedback': {
                    'content_id': 'feedback_1',
                    'html': '<p>Feedback</p>'
                },
                'labelled_as_correct': True,
                'param_changes': [],
                'refresher_exploration_id': None,
                'missing_prerequisite_skill_id': None
            },
            'rule_specs': [{
                'inputs': {
                    'x': 'Test'
                },
                'rule_type': 'Contains'
            }],
            'training_data': [],
            'tagged_misconception_id': None
        }
        question_state_dict = {
            'content': {
                'content_id': 'content_1',
                'html': 'Question 1'
            },
            'recorded_voiceovers': {
                'voiceovers_mapping': {}
            },
            'written_translations': {
                'translations_mapping': {
                    'explanation': {}
                }
            },
            'interaction': {
                'answer_groups': [answer_group],
                'confirmed_unclassified_answers': [],
                'customization_args': {},
                'default_outcome': {
                    'dest': None,
                    'feedback': {
                        'content_id': 'feedback_1',
                        'html': 'Correct Answer'
                    },
                    'param_changes': [],
                    'refresher_exploration_id': None,
                    'labelled_as_correct': True,
                    'missing_prerequisite_skill_id': None
                },
                'hints': [{
                    'hint_content': {
                        'content_id': 'hint_1',
                        'html': 'Hint 1'
                    }
                }],
                'solution': {
                    'correct_answer': 'This is the correct answer',
                    'answer_is_exclusive': False,
                    'explanation': {
                        'content_id': 'explanation_1',
                        'html': 'Solution explanation'
                    }
                },
                'id':
                'TextInput'
            },
            'param_changes': [],
            'solicit_answer_details': False,
            'classifier_model_id': None
        }
        question_model = question_models.QuestionModel(
            id='question_id',
            question_state_data=question_state_dict,
            language_code='en',
            version=0,
            linked_skill_ids=['skill_id'],
            question_state_data_schema_version=29)
        commit_cmd = question_domain.QuestionChange(
            {'cmd': question_domain.CMD_CREATE_NEW})
        commit_cmd_dicts = [commit_cmd.to_dict()]
        question_model.commit('user_id_admin', 'question model created',
                              commit_cmd_dicts)

        current_schema_version_swap = self.swap(
            feconf, 'CURRENT_STATE_SCHEMA_VERSION', 30)

        with current_schema_version_swap:
            question = question_fetchers.get_question_from_model(
                question_model)

        self.assertEqual(question.question_state_data_schema_version, 30)

        answer_groups = question.question_state_data.interaction.answer_groups
        self.assertEqual(answer_groups[0].tagged_skill_misconception_id, None)
Exemple #11
0
    def test_migrate_question_state_from_v33_to_v34(self):
        feedback_html_content = (
            '<p>Feedback</p><oppia-noninteractive-math raw_latex-with-value="'
            '&amp;quot;+,-,-,+&amp;quot;"></oppia-noninteractive-math>')
        answer_group = {
            'outcome': {
                'dest': 'abc',
                'feedback': {
                    'content_id': 'feedback_1',
                    'html': feedback_html_content
                },
                'labelled_as_correct': True,
                'param_changes': [],
                'refresher_exploration_id': None,
                'missing_prerequisite_skill_id': None
            },
            'rule_specs': [{
                'inputs': {
                    'x': ['A']
                },
                'rule_type': 'Equals'
            }],
            'training_data': [],
            'tagged_skill_misconception_id': None
        }
        question_state_dict = {
            'content': {
                'content_id': 'content_1',
                'html': 'Question 1'
            },
            'recorded_voiceovers': {
                'voiceovers_mapping': {}
            },
            'written_translations': {
                'translations_mapping': {
                    'explanation': {}
                }
            },
            'interaction': {
                'answer_groups': [answer_group],
                'confirmed_unclassified_answers': [],
                'customization_args': {
                    'choices': {
                        'value': ''
                    },
                    'showChoicesInShuffledOrder': {
                        'value': True
                    }
                },
                'default_outcome': {
                    'dest': None,
                    'feedback': {
                        'content_id': 'feedback_1',
                        'html': 'Correct Answer'
                    },
                    'param_changes': [],
                    'refresher_exploration_id': None,
                    'labelled_as_correct': True,
                    'missing_prerequisite_skill_id': None
                },
                'hints': [{
                    'hint_content': {
                        'content_id': 'hint_1',
                        'html': 'Hint 1'
                    }
                }],
                'solution': {},
                'id':
                'MultipleChoiceInput'
            },
            'param_changes': [],
            'solicit_answer_details': False,
            'classifier_model_id': None
        }
        expected_feeedback_html_content = (
            '<p>Feedback</p><oppia-noninteractive-math math_content-with-val'
            'ue="{&amp;quot;raw_latex&amp;quot;: &amp;quot;+,-,-,+&amp;quot;,'
            ' &amp;quot;svg_filename&amp;quot;: &amp;quot;&amp;quot;}"></oppi'
            'a-noninteractive-math>')
        question_model = (question_models.QuestionModel(
            id='question_id',
            question_state_data=question_state_dict,
            language_code='en',
            version=0,
            linked_skill_ids=['skill_id'],
            question_state_data_schema_version=33))
        commit_cmd = (question_domain.QuestionChange(
            {'cmd': question_domain.CMD_CREATE_NEW}))
        commit_cmd_dicts = [commit_cmd.to_dict()]
        question_model.commit('user_id_admin', 'question model created',
                              commit_cmd_dicts)

        current_schema_version_swap = self.swap(
            feconf, 'CURRENT_STATE_SCHEMA_VERSION', 34)

        with current_schema_version_swap:
            question = question_fetchers.get_question_from_model(
                question_model)

        self.assertEqual(question.question_state_data_schema_version, 34)

        migrated_answer_group = (
            question.question_state_data.interaction.answer_groups[0])
        self.assertEqual(migrated_answer_group.outcome.feedback.html,
                         expected_feeedback_html_content)
Exemple #12
0
 def _get_model_domain_object_instance(cls, item):
     return question_fetchers.get_question_from_model(item)