def test_skill_change_object_with_migrate_misconceptions_schema_to_latest_version( # pylint: disable=line-too-long self): skill_change_object = skill_domain.SkillChange({ 'cmd': 'migrate_misconceptions_schema_to_latest_version', 'from_version': 'from_version', 'to_version': 'to_version' }) self.assertEqual( skill_change_object.cmd, 'migrate_misconceptions_schema_to_latest_version') self.assertEqual(skill_change_object.from_version, 'from_version') self.assertEqual(skill_change_object.to_version, 'to_version')
def setUp(self): super(SkillCommitCmdMigrationOneOffJobTests, self).setUp() self.signup(self.ALBERT_EMAIL, self.ALBERT_NAME) self.albert_id = self.get_user_id_from_email(self.ALBERT_EMAIL) self.set_admins([self.ALBERT_NAME]) rubrics = [ skill_domain.Rubric( constants.SKILL_DIFFICULTIES[0], ['Explanation 1']), skill_domain.Rubric( constants.SKILL_DIFFICULTIES[1], ['Explanation 2']), skill_domain.Rubric( constants.SKILL_DIFFICULTIES[2], ['Explanation 3'])] skill = skill_domain.Skill.create_default_skill( self.SKILL_ID, 'A description', rubrics) skill_services.save_new_skill(self.albert_id, skill) skill_services.update_skill( self.albert_id, 'skill_id', [skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_RUBRICS, 'difficulty': constants.SKILL_DIFFICULTIES[0], 'explanations': ['New explanation'], }), skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': 'description', 'old_value': '', 'new_value': 'Test description' })], 'Changes.') self.model_instance_0 = ( skill_models.SkillCommitLogEntryModel.get_by_id( 'skill-skill_id-1')) self.model_instance_1 = ( skill_models.SkillCommitLogEntryModel.get_by_id( 'skill-skill_id-2')) self.process_and_flush_pending_mapreduce_tasks()
def test_skill_change_object_with_migrate_rubrics_schema_to_latest_version( self): skill_change_object = skill_domain.SkillChange({ 'cmd': 'migrate_rubrics_schema_to_latest_version', 'from_version': 'from_version', 'to_version': 'to_version' }) self.assertEqual( skill_change_object.cmd, 'migrate_rubrics_schema_to_latest_version') self.assertEqual(skill_change_object.from_version, 'from_version') self.assertEqual(skill_change_object.to_version, 'to_version')
def test_skill_change_object_with_add_skill_misconception(self): skill_change_object = skill_domain.SkillChange({ 'cmd': 'add_skill_misconception', 'new_misconception_dict': { 'id': 0, 'name': 'name', 'notes': '<p>notes</p>', 'feedback': '<p>default_feedback</p>'}, }) self.assertEqual(skill_change_object.cmd, 'add_skill_misconception') self.assertEqual( skill_change_object.new_misconception_dict, { 'id': 0, 'name': 'name', 'notes': '<p>notes</p>', 'feedback': '<p>default_feedback</p>'})
def test_cannot_add_rubric_with_invalid_difficulty(self): changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_RUBRICS, 'difficulty': 'invalid_difficulty', 'explanation': '<p>Explanation</p>' }) ] with self.assertRaisesRegexp( Exception, 'There is no rubric for the given difficulty.'): skill_services.update_skill(self.USER_ID, self.SKILL_ID, changelist, 'Added rubric.')
def test_skill_change_object_with_update_skill_contents_property(self): skill_change_object = skill_domain.SkillChange({ 'cmd': 'update_skill_contents_property', 'property_name': 'explanation', 'new_value': 'new_value', 'old_value': 'old_value' }) self.assertEqual(skill_change_object.cmd, 'update_skill_contents_property') self.assertEqual(skill_change_object.property_name, 'explanation') self.assertEqual(skill_change_object.new_value, 'new_value') self.assertEqual(skill_change_object.old_value, 'old_value')
def test_get_skill_from_model_with_latest_schemas_version(self) -> None: 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=2, rubric_schema_version=2, skill_contents_schema_version=2, all_questions_merged=False, skill_contents=skill_domain.SkillContents( # type: ignore[no-untyped-call] 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) skill = skill_fetchers.get_skill_from_model(model) self.assertEqual( skill.misconceptions_schema_version, feconf.CURRENT_MISCONCEPTIONS_SCHEMA_VERSION ) self.assertEqual( skill.skill_contents_schema_version, feconf.CURRENT_SKILL_CONTENTS_SCHEMA_VERSION ) self.assertEqual( skill.rubric_schema_version, feconf.CURRENT_RUBRIC_SCHEMA_VERSION )
def post(self): """Handles the POST request.""" if not constants.ENABLE_NEW_STRUCTURE_EDITORS: raise self.PageNotFoundException old_skill_id = self.payload.get('old_skill_id') new_skill_id = self.payload.get('new_skill_id') new_skill = skill_services.get_skill_by_id(new_skill_id, strict=False) if new_skill is None: raise self.PageNotFoundException( Exception('The new skill with the given id doesn\'t exist.')) old_skill = skill_services.get_skill_by_id(old_skill_id, strict=False) if old_skill is None: raise self.PageNotFoundException( Exception('The old skill with the given id doesn\'t exist.')) question_services.update_skill_ids_of_questions( old_skill_id, old_skill.description, new_skill_id) changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_SUPERSEDING_SKILL_ID), 'old_value': old_skill.superseding_skill_id, 'new_value': new_skill_id }), skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_ALL_QUESTIONS_MERGED), 'old_value': False, 'new_value': True }) ] skill_services.update_skill( self.user_id, old_skill_id, changelist, 'Marking the skill as having being merged successfully.') self.render_json({ 'merged_into_skill': new_skill_id })
def test_standard_operation(self): skill_services.update_skill(self.admin_id, '0', [ skill_domain.SkillChange({ 'cmd': 'update_skill_property', 'property_name': 'description', 'new_value': 'New description', 'old_value': 'description 0' }) ], 'Changes.') expected_output = [u'[u\'fully-validated SkillSummaryModel\', 3]'] self.process_and_flush_pending_mapreduce_tasks() self.run_job_and_check_output(expected_output, sort=False, literal_eval=False)
def test_set_merge_complete_for_skill(self): changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_SUPERSEDING_SKILL_ID), 'old_value': None, 'new_value': self.SKILL_ID }), skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_ALL_QUESTIONS_MERGED), 'old_value': False, 'new_value': True }) ] skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, 'Setting merge complete for skill.') skill = skill_services.get_skill_by_id(self.SKILL_ID) self.assertEqual(skill.version, 2) self.assertEqual(skill.all_questions_merged, True)
def test_cannot_update_skill_with_no_commit_message(self): changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': skill_domain.SKILL_PROPERTY_LANGUAGE_CODE, 'old_value': 'en', 'new_value': 'bn' }) ] with self.assertRaisesRegexp( Exception, 'Expected a commit message, received none.'): skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, '')
def test_skill_change_object_with_update_rubrics(self): skill_change_object = skill_domain.SkillChange({ 'cmd': 'update_rubrics', 'difficulty': constants.SKILL_DIFFICULTIES[0], 'explanations': ['<p>Explanation</p>'] }) self.assertEqual(skill_change_object.cmd, 'update_rubrics') self.assertEqual(skill_change_object.difficulty, constants.SKILL_DIFFICULTIES[0]) self.assertEqual(skill_change_object.explanations, ['<p>Explanation</p>'])
def test_skill_change_object_with_invalid_skill_misconception_property( self): with self.assertRaisesRegexp( utils.ValidationError, ( 'Value for property_name in cmd ' 'update_skill_misconceptions_property: invalid is not ' 'allowed')): skill_domain.SkillChange({ 'cmd': 'update_skill_misconceptions_property', 'misconception_id': 'id', 'property_name': 'invalid', 'old_value': 'old_value', 'new_value': 'new_value', })
def test_skill_change_object_with_extra_attribute_in_cmd(self): with self.assertRaisesRegexp( utils.ValidationError, ('The following extra attributes are present: invalid')): skill_domain.SkillChange({ 'cmd': 'add_skill_misconception', 'new_misconception_dict': { 'id': 0, 'name': 'name', 'notes': '<p>notes</p>', 'feedback': '<p>default_feedback</p>' }, 'invalid': 'invalid' })
def test_normal_user_cannot_update_skill_property(self): changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': skill_domain.SKILL_PROPERTY_DESCRIPTION, 'old_value': 'Description', 'new_value': 'New description' }) ] with self.assertRaisesRegexp( Exception, 'The user does not have enough rights to edit the ' 'skill description.'): skill_services.update_skill(self.user_id_a, self.SKILL_ID, changelist, 'Change description.')
def test_merge_skill(self): changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_SUPERSEDING_SKILL_ID), 'old_value': '', 'new_value': 'TestSkillId' }), skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_ALL_QUESTIONS_MERGED), 'old_value': None, 'new_value': False }) ] skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, 'Merging skill.') skill = skill_services.get_skill_by_id(self.SKILL_ID) self.assertEqual(skill.version, 2) self.assertEqual(skill.superseding_skill_id, 'TestSkillId') self.assertEqual(skill.all_questions_merged, False)
def test_update_skill(self): changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_ADD_SKILL_MISCONCEPTION, 'id': self.MISCONCEPTION_ID_2 }), skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_MISCONCEPTIONS_PROPERTY, 'property_name': ( skill_domain.SKILL_MISCONCEPTIONS_PROPERTY_NAME), 'id': self.MISCONCEPTION_ID_2, 'old_value': '', 'new_value': 'Name' }) ] skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, 'Updated misconception name.') skill = skill_services.get_skill_by_id(self.SKILL_ID) skill_summary = skill_services.get_skill_summary_by_id(self.SKILL_ID) self.assertEqual(skill_summary.misconception_count, 2) self.assertEqual(skill_summary.version, 2) self.assertEqual(skill.version, 2) self.assertEqual(skill.misconceptions[1].name, 'Name')
def test_cannot_update_misconception_feedback_with_invalid_id(self): changelist = [skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_MISCONCEPTIONS_PROPERTY, 'property_name': ( skill_domain.SKILL_MISCONCEPTIONS_PROPERTY_FEEDBACK), 'misconception_id': 'invalid_id', 'old_value': 'default_feedback', 'new_value': 'new feedback' })] with self.assertRaisesRegexp( Exception, 'There is no misconception with the given id.'): skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, 'Updated misconception feedback.')
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)
def test_skill_change_object_with_update_skill_misconceptions_property( self): skill_change_object = skill_domain.SkillChange({ 'cmd': 'update_skill_misconceptions_property', 'misconception_id': 'id', 'property_name': 'name', 'new_value': 'new_value', 'old_value': 'old_value' }) self.assertEqual( skill_change_object.cmd, 'update_skill_misconceptions_property') self.assertEqual(skill_change_object.misconception_id, 'id') self.assertEqual(skill_change_object.property_name, 'name') self.assertEqual(skill_change_object.new_value, 'new_value') self.assertEqual(skill_change_object.old_value, 'old_value')
def test_cannot_update_misconception_must_be_addressed_with_invalid_id( self): changelist = [skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_MISCONCEPTIONS_PROPERTY, 'property_name': ( skill_domain.SKILL_MISCONCEPTIONS_PROPERTY_MUST_BE_ADDRESSED), 'misconception_id': 'invalid_id', 'old_value': False, 'new_value': True })] with self.assertRaisesRegexp( Exception, 'There is no misconception with the given id.'): skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, 'Updated misconception must_be_addressed.')
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 test_get_skill_from_model_with_invalid_rubric_schema_version( self ) -> None: 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=2, rubric_schema_version=0, skill_contents_schema_version=2, all_questions_merged=False, skill_contents=skill_domain.SkillContents( # type: ignore[no-untyped-call] 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( # type: ignore[no-untyped-call] Exception, 'Sorry, we can only process v1-v%d rubric schemas at ' 'present.' % feconf.CURRENT_RUBRIC_SCHEMA_VERSION): skill_fetchers.get_skill_from_model(model)
def test_get_merged_skill_ids(self): skill_ids = skill_services.get_merged_skill_ids() self.assertEqual(len(skill_ids), 0) changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_SUPERSEDING_SKILL_ID), 'old_value': '', 'new_value': 'TestSkillId' }) ] skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, 'Merging skill.') skill_ids = skill_services.get_merged_skill_ids() self.assertEqual(len(skill_ids), 1) self.assertEqual(skill_ids[0], self.SKILL_ID)
def test_delete_skill_misconception(self): skill = skill_services.get_skill_by_id(self.SKILL_ID) self.assertEqual(len(skill.misconceptions), 1) self.assertEqual(skill.misconceptions[0].id, self.MISCONCEPTION_ID_1) changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_DELETE_SKILL_MISCONCEPTION, 'misconception_id': self.MISCONCEPTION_ID_1, }) ] skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, 'Delete misconception.') skill = skill_services.get_skill_by_id(self.SKILL_ID) self.assertEqual(skill.misconceptions, [])
def test_get_skill_by_id_with_different_versions(self) -> None: changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': skill_domain.SKILL_PROPERTY_LANGUAGE_CODE, 'old_value': 'en', 'new_value': 'bn' }) ] skill_services.update_skill( # type: ignore[no-untyped-call] self.USER_ID, self.skill_id, changelist, 'update language code') skill = skill_fetchers.get_skill_by_id(self.skill_id, version=1) self.assertEqual(skill.id, self.skill_id) self.assertEqual(skill.language_code, 'en') skill = skill_fetchers.get_skill_by_id(self.skill_id, version=2) self.assertEqual(skill.id, self.skill_id) self.assertEqual(skill.language_code, 'bn')
def test_update_skill_description_updates_skill_opportunity(self): self.save_new_skill(self.SKILL_ID, self.USER_ID, 'skill_description') changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_DESCRIPTION), 'old_value': 'skill_description', 'new_value': 'new_description' }) ] skill_services.update_skill( self.admin_id, self.SKILL_ID, changelist, 'Updated misconception name.') skill_opportunities, _, _ = ( opportunity_services.get_skill_opportunities(None)) opportunity = skill_opportunities[0] self.assertEqual(opportunity['id'], self.SKILL_ID) self.assertEqual(opportunity['skill_description'], 'new_description')
def test_get_skill_from_model_with_invalid_misconceptions_schema_version( self): skill_services.create_new_skill_rights('skill_id', self.user_id_admin) commit_cmd = skill_domain.SkillChange( {'cmd': skill_domain.CMD_CREATE_NEW}) model = skill_models.SkillModel(id='skill_id', description='description', language_code='en', misconceptions=[], next_misconception_id=0, misconceptions_schema_version=0, skill_contents_schema_version=1, all_questions_merged=False) commit_cmd_dicts = [commit_cmd.to_dict()] model.commit(self.user_id_admin, 'skill model created', commit_cmd_dicts) with self.assertRaisesRegexp( Exception, 'Sorry, we can only process v1-v%d misconception schemas at ' 'present.' % feconf.CURRENT_MISCONCEPTIONS_SCHEMA_VERSION): skill_services.get_skill_from_model(model)
def post(self): """Handles the POST request.""" if not constants.ENABLE_NEW_STRUCTURE_EDITORS: raise self.PageNotFoundException old_skill_id = self.payload.get('old_skill_id') new_skill_id = self.payload.get('new_skill_id') question_services.update_skill_ids_of_questions( old_skill_id, new_skill_id) changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_PROPERTY, 'property_name': ( skill_domain.SKILL_PROPERTY_ALL_QUESTIONS_MERGED), 'old_value': False, 'new_value': True }) ] skill_services.update_skill( self.user_id, old_skill_id, changelist, 'Marking the skill as having being merged successfully.') self.render_json({ 'merged_into_skill': new_skill_id })
def test_update_skill_explanation(self): skill = skill_services.get_skill_by_id(self.SKILL_ID) old_explanation = {'content_id': '1', 'html': '<p>Explanation</p>'} new_explanation = {'content_id': '1', 'html': '<p>New explanation</p>'} self.assertEqual( skill.skill_contents.explanation.to_dict(), old_explanation) changelist = [ skill_domain.SkillChange({ 'cmd': skill_domain.CMD_UPDATE_SKILL_CONTENTS_PROPERTY, 'property_name': ( skill_domain.SKILL_CONTENTS_PROPERTY_EXPLANATION), 'old_value': old_explanation, 'new_value': new_explanation }) ] skill_services.update_skill( self.USER_ID, self.SKILL_ID, changelist, 'Change explanation.') skill = skill_services.get_skill_by_id(self.SKILL_ID) self.assertEqual( skill.skill_contents.explanation.to_dict(), new_explanation)