def test_cannot_update_question_with_no_change_list(self):
     with self.assertRaisesRegexp(
             Exception,
             'Unexpected error: received an invalid change list when trying to '
             'save question'):
         question_services.update_question(self.editor_id, self.question_id,
                                           [], 'updated question data')
Example #2
0
    def put(self, question_id):
        """Updates properties of the given question."""
        commit_message = self.payload.get('commit_message')

        if not commit_message:
            raise self.PageNotFoundException

        if (commit_message is not None
                and len(commit_message) > constants.MAX_COMMIT_MESSAGE_LENGTH):
            raise self.InvalidInputException(
                'Commit messages must be at most %s characters long.' %
                constants.MAX_COMMIT_MESSAGE_LENGTH)

        if not self.payload.get('change_list'):
            raise self.PageNotFoundException
        change_list = [
            question_domain.QuestionChange(change)
            for change in self.payload.get('change_list')
        ]

        for change in change_list:
            if (change.cmd ==
                    question_domain.CMD_CREATE_NEW_FULLY_SPECIFIED_QUESTION):
                raise self.InvalidInputException

        question_services.update_question(self.user_id, question_id,
                                          change_list, commit_message)

        question_dict = question_services.get_question_by_id(
            question_id).to_dict()
        self.render_json({'question_dict': question_dict})
Example #3
0
    def put(self, question_id):
        """Updates properties of the given question."""
        if not constants.ENABLE_NEW_STRUCTURES:
            raise self.PageNotFoundException

        question = question_services.get_question_by_id(question_id,
                                                        strict=False)

        if question is None:
            raise self.PageNotFoundException(
                'The question with the given id doesn\'t exist.')

        commit_message = self.payload.get('commit_message')
        if not question_id:
            raise self.PageNotFoundException
        if not commit_message:
            raise self.PageNotFoundException
        if not self.payload.get('change_list'):
            raise self.PageNotFoundException
        change_list = [
            question_domain.QuestionChange(change)
            for change in self.payload.get('change_list')
        ]

        for change in change_list:
            if (change.cmd ==
                    question_domain.CMD_CREATE_NEW_FULLY_SPECIFIED_QUESTION):
                raise self.InvalidInputException

        question_services.update_question(self.user_id, question_id,
                                          change_list, commit_message)

        question_dict = question_services.get_question_by_id(
            question_id).to_dict()
        return self.render_json({'question_dict': question_dict})
    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_services.get_question_by_id(item.id)
        try:
            question.validate()
        except Exception as e:
            logging.error('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)
Example #5
0
    def test_update_question(self):
        question_data = self._create_valid_question_data('ABC')
        question_id = 'dummy'
        question_data_schema_version = 1
        language_code = 'en'
        question = question_domain.Question(question_id, question_data,
                                            question_data_schema_version,
                                            language_code)

        new_question_data = self._create_valid_question_data('DEF')
        question_id = question_services.add_question(self.owner_id, question)
        change_dict = {
            'cmd': 'update_question_property',
            'property_name': 'question_data',
            'new_value': new_question_data,
            'old_value': question_data
        }
        change_list = [question_domain.QuestionChange(change_dict)]

        question_services.update_question(self.owner_id, question_id,
                                          change_list,
                                          ('updated question data'))

        model = question_models.QuestionModel.get(question_id)
        self.assertEqual(model.question_data, new_question_data)
        self.assertEqual(model.question_data_schema_version,
                         question_data_schema_version)
        self.assertEqual(model.language_code, language_code)
Example #6
0
    def test_update_question(self):
        state = exp_domain.State.create_default_state('ABC')
        question_data = state.to_dict()
        question_id = 'dummy'
        title = 'A Question'
        question_data_schema_version = 1
        collection_id = 'col1'
        language_code = 'en'
        question = question_domain.Question(question_id, title, question_data,
                                            question_data_schema_version,
                                            collection_id, language_code)

        question_id = question_services.add_question(self.owner_id, question)
        change_dict = {
            'cmd': 'update_question_property',
            'property_name': 'title',
            'new_value': 'ABC',
            'old_value': 'A Question'
        }
        change_list = [question_domain.QuestionChange(change_dict)]
        question_services.update_question(self.owner_id, question_id,
                                          change_list, 'updated title')

        model = question_models.QuestionModel.get(question_id)
        self.assertEqual(model.title, 'ABC')
        self.assertEqual(model.question_data, question_data)
        self.assertEqual(model.question_data_schema_version,
                         question_data_schema_version)
        self.assertEqual(model.collection_id, collection_id)
        self.assertEqual(model.language_code, language_code)
Example #7
0
    def test_audit_job_failure(self):
        """Test that the audit job catches errors that would otherwise occur
        during the migration.
        """
        swap_states_schema_36 = self.swap(
            feconf, 'CURRENT_STATE_SCHEMA_VERSION', 36)
        with swap_states_schema_36:
            self.save_new_question(
                self.QUESTION_ID, self.albert_id,
                self._create_valid_question_data('ABC'), [self.skill_id])

        # Bring the main question to the latest schema.
        latest_schema_version = python_utils.UNICODE(
            feconf.CURRENT_STATE_SCHEMA_VERSION)
        migration_change_list = [
            question_domain.QuestionChange({
                'cmd': (
                    question_domain.CMD_MIGRATE_STATE_SCHEMA_TO_LATEST_VERSION
                ),
                'from_version': '37',
                'to_version': latest_schema_version
            })
        ]
        question_services.update_question(
            self.albert_id, self.QUESTION_ID, migration_change_list,
            'Ran Question Migration job.')
        question_model = question_models.QuestionModel.get(self.QUESTION_ID)
        self.assertEqual(
            question_model.question_state_data_schema_version,
            feconf.CURRENT_STATE_SCHEMA_VERSION)

        # Make a mock conversion function that raises an error when trying to
        # convert the old snapshot.
        mock_conversion = classmethod(
            lambda cls, question_dict: question_dict['property_that_dne'])

        with self.swap(
            question_domain.Question, '_convert_state_v36_dict_to_v37_dict',
            mock_conversion
        ):
            job_id = (
                question_jobs_one_off.QuestionSnapshotsMigrationAuditJob.
                create_new())
            question_jobs_one_off.QuestionSnapshotsMigrationAuditJob.enqueue(
                job_id)
            self.process_and_flush_pending_mapreduce_tasks()

        actual_output = (
            question_jobs_one_off.QuestionSnapshotsMigrationAuditJob.get_output(
                job_id))
        expected_output_message = (
            u'[u\'MIGRATION_ERROR\', [u"Question snapshot %s-1 failed '
            'migration to state v37: u\'property_that_dne\'"]]'
            % self.QUESTION_ID
        )
        self.assertIn(expected_output_message, actual_output)
Example #8
0
    def test_cannot_update_question_with_no_commit_message(self):
        new_question_data = self._create_valid_question_data('DEF')
        change_dict = {
            'cmd': 'update_question_property',
            'property_name': 'question_state_data',
            'new_value': new_question_data.to_dict(),
            'old_value': self.question.question_state_data.to_dict()
        }
        change_list = [question_domain.QuestionChange(change_dict)]

        with self.assertRaisesRegexp(
            Exception, 'Expected a commit message, received none.'):
            question_services.update_question(
                self.editor_id, self.question_id, change_list, None)
Example #9
0
 def put(self, question_id):
     """Handles PUT requests."""
     commit_message = self.payload.get('commit_message')
     if not question_id:
         raise self.PageNotFoundException
     if not commit_message:
         raise self.PageNotFoundException
     if not self.payload.get('change_list'):
         raise self.PageNotFoundException
     change_list = [
         question_domain.QuestionChange(change)
         for change in json.loads(self.payload.get('change_list'))
     ]
     question_services.update_question(self.user_id, question_id,
                                       change_list, commit_message)
     return self.render_json({'question_id': question_id})
Example #10
0
    def test_audit_job_success(self):
        """Test that the audit job runs correctly on snapshots that use a
        previous state schema.
        """
        swap_states_schema_36 = self.swap(
            feconf, 'CURRENT_STATE_SCHEMA_VERSION', 36)
        with swap_states_schema_36:
            question = self.save_new_question(
                self.QUESTION_ID, self.albert_id,
                self._create_valid_question_data('ABC'), [self.skill_id])
        self.assertLess(question.question_state_data_schema_version, 37)

        # Bring the main question to schema version 37.
        migration_change_list = [
            question_domain.QuestionChange({
                'cmd': (
                    question_domain.CMD_MIGRATE_STATE_SCHEMA_TO_LATEST_VERSION
                ),
                'from_version': '36',
                'to_version': '37'
            })
        ]
        swap_states_schema_37 = self.swap(
            feconf, 'CURRENT_STATE_SCHEMA_VERSION', 37)
        with swap_states_schema_37:
            question_services.update_question(
                self.albert_id, self.QUESTION_ID, migration_change_list,
                'Ran Question Migration job.')
            question_model = question_models.QuestionModel.get(self.QUESTION_ID)
            self.assertEqual(
                question_model.question_state_data_schema_version, 37)

            job_id = (
                question_jobs_one_off.QuestionSnapshotsMigrationAuditJob.
                create_new())
            question_jobs_one_off.QuestionSnapshotsMigrationAuditJob.enqueue(
                job_id)
            self.process_and_flush_pending_mapreduce_tasks()

        actual_output = (
            question_jobs_one_off.QuestionSnapshotsMigrationAuditJob.get_output(
                job_id))
        expected_output = [
            '[u\'SUCCESS\', 1]',
            '[u\'SUCCESS - Snapshot is already at latest schema version\', 1]'
        ]
        self.assertEqual(sorted(actual_output), sorted(expected_output))
Example #11
0
    def test_update_question_language_code(self):
        self.assertEqual(self.question.language_code, 'en')
        change_dict = {
            'cmd': 'update_question_property',
            'property_name': 'language_code',
            'new_value': 'bn',
            'old_value': 'en'
        }
        change_list = [question_domain.QuestionChange(change_dict)]

        question_services.update_question(
            self.editor_id, self.question_id, change_list,
            'updated question language code')

        question = question_services.get_question_by_id(self.question_id)
        self.assertEqual(question.language_code, 'bn')
        self.assertEqual(question.version, 2)
Example #12
0
    def test_update_question(self):
        new_question_data = self._create_valid_question_data('DEF')
        change_dict = {
            'cmd': 'update_question_property',
            'property_name': 'question_state_data',
            'new_value': new_question_data.to_dict(),
            'old_value': self.question.question_state_data.to_dict()
        }
        change_list = [question_domain.QuestionChange(change_dict)]

        question_services.update_question(self.editor_id, self.question_id,
                                          change_list, 'updated question data')

        question = question_services.get_question_by_id(self.question_id)
        self.assertEqual(question.question_state_data.to_dict(),
                         new_question_data.to_dict())
        self.assertEqual(question.version, 2)
Example #13
0
    def test_cannot_update_question_with_invalid_change_list(self):
        observed_log_messages = []

        def _mock_logging_function(msg, *args):
            """Mocks logging.error()."""
            observed_log_messages.append(msg % args)

        logging_swap = self.swap(logging, 'error', _mock_logging_function)
        assert_raises_context_manager = self.assertRaises(Exception)

        with logging_swap, assert_raises_context_manager:
            question_services.update_question(
                self.editor_id, self.question_id, 'invalid_change_list',
                'updated question language code')

        self.assertEqual(len(observed_log_messages), 1)
        self.assertRegexpMatches(
            observed_log_messages[0], 'object has no attribute \'cmd\' %s '
            'invalid_change_list' % self.question_id)
    def test_migration_job_succeeds_on_default_question(self):
        swap_states_schema_version_37 = self.swap(
            feconf, 'CURRENT_STATE_SCHEMA_VERSION', 37)
        with swap_states_schema_version_37:
            self.save_new_question(
                self.QUESTION_ID, self.albert_id,
                self._create_valid_question_data('ABC'), [self.skill_id])

        # Bring the main question to schema version 38.
        migration_change_list = [
            question_domain.QuestionChange({
                'cmd': (
                    question_domain.CMD_MIGRATE_STATE_SCHEMA_TO_LATEST_VERSION
                ),
                'from_version': '37',
                'to_version': '38'
            })
        ]
        swap_states_schema_version_38 = self.swap(
            feconf, 'CURRENT_STATE_SCHEMA_VERSION', 38)
        with swap_states_schema_version_38:
            question_services.update_question(
                self.albert_id, self.QUESTION_ID, migration_change_list,
                'Ran Question Migration job.')

            job_id = (
                question_jobs_one_off.QuestionSnapshotsMigrationJob.
                create_new())
            question_jobs_one_off.QuestionSnapshotsMigrationJob.enqueue(
                job_id)
            self.process_and_flush_pending_mapreduce_tasks()

        actual_output = (
            question_jobs_one_off.QuestionSnapshotsMigrationJob.get_output(
                job_id))
        expected_output = [
            '[u\'SUCCESS - Model saved\', 1]',
            '[u\'SUCCESS - Model upgraded\', 1]',
            '[u\'SUCCESS - Snapshot is already at latest schema version\', 1]']
        self.assertEqual(sorted(actual_output), sorted(expected_output))
Example #15
0
    def test_update_question(self):
        state = exp_domain.State.create_default_state('ABC')
        question_data = state.to_dict()
        question_id = 'dummy'
        title = 'A Question'
        question_data_schema_version = 1
        collection_id = 'col1'
        language_code = 'en'
        question = question_domain.Question(question_id, title, question_data,
                                            question_data_schema_version,
                                            collection_id, language_code)

        question_id = question_services.add_question(self.owner_id, question)
        change_dict = {
            'cmd': 'update_question_property',
            'property_name': 'title',
            'new_value': 'ABC',
            'old_value': 'A Question'
        }
        change_list = [question_domain.QuestionChange(change_dict)]
        with self.assertRaisesRegexp(
                Exception, ('The question with ID %s is not present'
                            ' in the given collection' % question_id)):
            question_services.update_question(self.owner_id, 'random',
                                              question_id, change_list,
                                              'updated')

        question_services.update_question(self.owner_id, collection_id,
                                          question_id, change_list,
                                          ('updated title'))

        model = question_models.QuestionModel.get(question_id)
        self.assertEqual(model.title, 'ABC')
        self.assertEqual(model.question_data, question_data)
        self.assertEqual(model.question_data_schema_version,
                         question_data_schema_version)
        self.assertEqual(model.collection_id, collection_id)
        self.assertEqual(model.language_code, language_code)
Example #16
0
    def put(self, question_id):
        """Updates properties of the given question."""
        commit_message = self.payload.get('commit_message')

        if not commit_message:
            raise self.PageNotFoundException
        if not self.payload.get('change_list'):
            raise self.PageNotFoundException
        change_list = [
            question_domain.QuestionChange(change)
            for change in self.payload.get('change_list')
        ]

        for change in change_list:
            if (change.cmd ==
                    question_domain.CMD_CREATE_NEW_FULLY_SPECIFIED_QUESTION):
                raise self.InvalidInputException

        question_services.update_question(self.user_id, question_id,
                                          change_list, commit_message)

        question_dict = question_services.get_question_by_id(
            question_id).to_dict()
        self.render_json({'question_dict': question_dict})