def untag_deleted_misconceptions(committer_id, skill_id, skill_description): """Untags deleted misconceptions from questions belonging to a skill with the provided skill_id. Args: committer_id: str. The id of the user who triggered the update. skill_id: str. The skill id. skill_description: str. The description of the skill. """ question_skill_links = get_question_skill_links_of_skill( skill_id, skill_description) question_ids = [model.question_id for model in question_skill_links] questions = question_fetchers.get_questions_by_ids(question_ids) skill = skill_fetchers.get_skill_by_id(skill_id) skill_misconception_ids = ( [ skill.generate_skill_misconception_id(misconception.id) for misconception in skill.misconceptions ] ) for question in questions: change_list = [] inapplicable_skill_misconception_ids = ( question.inapplicable_skill_misconception_ids) deleted_inapplicable_skill_misconception_ids = ( utils.compute_list_difference( inapplicable_skill_misconception_ids, skill_misconception_ids)) for misconception_id in deleted_inapplicable_skill_misconception_ids: old_misconception_ids = list( question.inapplicable_skill_misconception_ids) question.inapplicable_skill_misconception_ids.remove( misconception_id) change_list.append(question_domain.QuestionChange({ 'cmd': 'update_question_property', 'property_name': 'inapplicable_skill_misconception_ids', 'new_value': question.inapplicable_skill_misconception_ids, 'old_value': old_misconception_ids })) old_question_state_data_dict = question.question_state_data.to_dict() answer_groups = ( list(question.question_state_data.interaction.answer_groups)) for i in python_utils.RANGE(len(answer_groups)): tagged_skill_misconception_id = ( answer_groups[i].to_dict()['tagged_skill_misconception_id']) if tagged_skill_misconception_id not in skill_misconception_ids: answer_groups[i].tagged_skill_misconception_id = None question.question_state_data.interaction.answer_groups = answer_groups change_list.append(question_domain.QuestionChange({ 'cmd': 'update_question_property', 'property_name': 'question_state_data', 'new_value': question.question_state_data.to_dict(), 'old_value': old_question_state_data_dict })) update_question( committer_id, question.id, change_list, 'Untagged deleted misconception id.')
def untag_deleted_misconceptions( committer_id, skill_id, skill_description, deleted_skill_misconception_ids): """Untags deleted misconceptions from questions belonging to a skill with the provided skill_id. Args: committer_id: str. The id of the user who triggered the update. skill_id: str. The skill id. skill_description: str. The description of the skill. deleted_skill_misconception_ids: list(str). The skill misconception ids of deleted misconceptions. The list items take the form <skill_id>-<misconception_id>. """ question_skill_links = get_question_skill_links_of_skill( skill_id, skill_description) question_ids = [model.question_id for model in question_skill_links] questions = question_fetchers.get_questions_by_ids(question_ids) for question in questions: change_list = [] inapplicable_skill_misconception_ids = ( question.inapplicable_skill_misconception_ids) deleted_inapplicable_skill_misconception_ids = ( list( set(deleted_skill_misconception_ids) & set(inapplicable_skill_misconception_ids))) if deleted_inapplicable_skill_misconception_ids: new_inapplicable_skill_misconception_ids = ( utils.compute_list_difference( question.inapplicable_skill_misconception_ids, deleted_inapplicable_skill_misconception_ids)) change_list.append(question_domain.QuestionChange({ 'cmd': 'update_question_property', 'property_name': 'inapplicable_skill_misconception_ids', 'new_value': new_inapplicable_skill_misconception_ids, 'old_value': question.inapplicable_skill_misconception_ids })) old_question_state_data_dict = question.question_state_data.to_dict() answer_groups = ( list(question.question_state_data.interaction.answer_groups)) for answer_group in answer_groups: tagged_skill_misconception_id = ( answer_group.to_dict()['tagged_skill_misconception_id']) if (tagged_skill_misconception_id in deleted_skill_misconception_ids): answer_group.tagged_skill_misconception_id = None question.question_state_data.interaction.answer_groups = answer_groups change_list.append(question_domain.QuestionChange({ 'cmd': 'update_question_property', 'property_name': 'question_state_data', 'new_value': question.question_state_data.to_dict(), 'old_value': old_question_state_data_dict })) update_question( committer_id, question.id, change_list, 'Untagged deleted skill misconception ids.')
def test_pre_update_validate_change_question_dict(self): change = { 'cmd': question_domain.CMD_CREATE_NEW_FULLY_SPECIFIED_QUESTION, 'question_dict': { 'question_state_data': self._create_valid_question_data( 'default_state').to_dict(), 'language_code': 'en', 'question_state_data_schema_version': ( feconf.CURRENT_STATE_SCHEMA_VERSION) }, 'skill_id': 'skill_1' } suggestion = suggestion_registry.SuggestionAddQuestion( 'exploration.exp1.thread1', 'exp1', 1, suggestion_models.STATUS_ACCEPTED, self.author_id, self.reviewer_id, change, 'question.topic_1', self.fake_date) with self.assertRaisesRegexp( Exception, 'The new change question_dict must not be equal to the ' 'old question_dict'): suggestion.pre_update_validate( question_domain.QuestionChange(change))
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)
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)
def test_pre_update_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) change = { 'cmd': question_domain.CMD_CREATE_NEW_FULLY_SPECIFIED_QUESTION, 'question_dict': { 'question_state_data': self._create_valid_question_data( 'default_state').to_dict(), 'language_code': 'en', 'question_state_data_schema_version': ( feconf.CURRENT_STATE_SCHEMA_VERSION) }, 'skill_id': 'skill_2' } with self.assertRaisesRegexp( Exception, 'The new change skill_id must be equal to skill_1'): suggestion.pre_update_validate( question_domain.QuestionChange(change))
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)
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 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})
def test_create_new(self): """Test to verify __init__ method of the Question Change object when cmd is create_new. """ change_dict = {'cmd': 'create_new'} observed_object = question_domain.QuestionChange( change_dict=change_dict, ) self.assertEqual('create_new', observed_object.cmd)
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)
def test_migrate_state_schema_to_latest_version(self): """Test to verify __init__ method of the Question Change object when cmd is migrate_state_schema_to_latest_version. """ change_dict = { 'cmd': 'migrate_state_schema_to_latest_version', 'from_version': 0, 'to_version': 10, } observed_object = question_domain.QuestionChange( change_dict=change_dict, ) self.assertEqual(0, observed_object.from_version) self.assertEqual(10, observed_object.to_version)
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)
def test_create_new_fully_specified_question(self): """Test to verify __init__ method of the Question Change object when cmd is create_new_fully_specified_question. """ change_dict = { 'cmd': 'create_new_fully_specified_question', 'question_dict': {}, 'skill_id': '10', } observed_object = question_domain.QuestionChange( change_dict=change_dict, ) self.assertEqual('10', observed_object.skill_id) self.assertEqual({}, observed_object.question_dict)
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})
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))
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)
def test_update_question_property(self): """Test to verify __init__ method of the Question Change object when cmd is update_question_property. """ change_dict = { 'cmd': 'update_question_property', 'property_name': 'question_state_data', 'new_value': 'new_value', 'old_value': 'old_value' } observed_object = question_domain.QuestionChange( change_dict=change_dict) self.assertEqual('update_question_property', observed_object.cmd) self.assertEqual('question_state_data', observed_object.property_name) self.assertEqual('new_value', observed_object.new_value) self.assertEqual('old_value', observed_object.old_value)
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)
def __init__( # pylint: disable=super-init-not-called self, suggestion_id, target_id, target_version_at_submission, status, author_id, final_reviewer_id, change, score_category, last_updated): """Initializes an object of type SuggestionAddQuestion corresponding to the SUGGESTION_TYPE_ADD_QUESTION choice. """ self.suggestion_id = suggestion_id self.suggestion_type = suggestion_models.SUGGESTION_TYPE_ADD_QUESTION self.target_type = suggestion_models.TARGET_TYPE_TOPIC self.target_id = target_id self.target_version_at_submission = target_version_at_submission self.status = status self.author_id = author_id self.final_reviewer_id = final_reviewer_id self.change = question_domain.QuestionChange(change) self.score_category = score_category self.last_updated = last_updated
def test_to_dict(self): """Test to verify to_dict method of the Question Change object.""" expected_object_dict = { 'cmd': 'update_question_property', 'property_name': 'question_state_data', 'new_value': 'new_value', 'old_value': 'old_value', } change_dict = { 'cmd': 'update_question_property', 'property_name': 'question_state_data', 'new_value': 'new_value', 'old_value': 'old_value', } observed_object = question_domain.QuestionChange( change_dict=change_dict, ) self.assertEqual(expected_object_dict, observed_object.to_dict())
def _update_linked_skill_ids_of_question( user_id, question_id, new_linked_skill_ids, old_linked_skill_ids): """Updates the question linked_skill ids in the Question model. Args: user_id: str. ID of the creator. question_id: str. ID of the question linked to the skill. new_linked_skill_ids: list(str). New linked skill IDs of the question. old_linked_skill_ids: list(str). Current linked skill IDs of the question. """ change_dict = { 'cmd': 'update_question_property', 'property_name': 'linked_skill_ids', 'new_value': new_linked_skill_ids, 'old_value': old_linked_skill_ids } change_list = [question_domain.QuestionChange(change_dict)] update_question( user_id, question_id, change_list, 'updated linked skill ids')
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))
def __init__( # pylint: disable=super-init-not-called self, suggestion_id, target_id, target_version_at_submission, status, author_id, final_reviewer_id, change, score_category, last_updated): """Initializes an object of type SuggestionAddQuestion corresponding to the SUGGESTION_TYPE_ADD_QUESTION choice. """ self.suggestion_id = suggestion_id self.suggestion_type = suggestion_models.SUGGESTION_TYPE_ADD_QUESTION self.target_type = suggestion_models.TARGET_TYPE_SKILL self.target_id = target_id self.target_version_at_submission = target_version_at_submission self.status = status self.author_id = author_id self.final_reviewer_id = final_reviewer_id self.change = question_domain.QuestionChange(change) # Update question_state_data_schema_version here instead of surfacing # the version in the frontend. self.change.question_dict['question_state_data_schema_version'] = ( feconf.CURRENT_STATE_SCHEMA_VERSION) self.score_category = score_category self.last_updated = last_updated
def test_pre_update_validate_change_cmd(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) change = { 'cmd': question_domain.CMD_UPDATE_QUESTION_PROPERTY, 'property_name': question_domain.QUESTION_PROPERTY_LANGUAGE_CODE, 'new_value': 'bn', 'old_value': 'en' } with self.assertRaisesRegexp( Exception, 'The new change cmd must be equal to ' 'create_new_fully_specified_question'): suggestion.pre_update_validate( question_domain.QuestionChange(change))
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)
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})
def test_migrate_question_state_from_v33_to_v34(self): feedback_html_content = ( '<p>Feedback</p><oppia-noninteractive-math raw_latex-with-value="' '&quot;+,-,-,+&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="{&quot;raw_latex&quot;: &quot;+,-,-,+&quot;,' ' &quot;svg_filename&quot;: &quot;&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)
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)