def _create_exploration( committer_id, exploration, commit_message, commit_cmds, cloned_from): """Ensures that rights for a new exploration are saved first. This is because _save_exploration() depends on the rights object being present to tell it whether to do strict validation or not. """ # This line is needed because otherwise a rights object will be created, # but the creation of an exploration object will fail. exploration.validate() rights_manager.create_new_exploration_rights( exploration.id, committer_id, cloned_from) model = exp_models.ExplorationModel( id=exploration.id, category=exploration.category, title=exploration.title, init_state_name=exploration.init_state_name, states={ state_name: state.to_dict() for (state_name, state) in exploration.states.iteritems()}, param_specs=exploration.param_specs_dict, param_changes=exploration.param_change_dicts, default_skin=exploration.default_skin ) model.commit(committer_id, commit_message, commit_cmds) exploration.version += 1
def _create_exploration( committer_id, exploration, commit_message, commit_cmds): """Ensures that rights for a new exploration are saved first. This is because _save_exploration() depends on the rights object being present to tell it whether to do strict validation or not. """ # This line is needed because otherwise a rights object will be created, # but the creation of an exploration object will fail. exploration.validate(allow_null_interaction=True) rights_manager.create_new_exploration_rights(exploration.id, committer_id) model = exp_models.ExplorationModel( id=exploration.id, category=exploration.category, title=exploration.title, objective=exploration.objective, language_code=exploration.language_code, skill_tags=exploration.skill_tags, blurb=exploration.blurb, author_notes=exploration.author_notes, default_skin=exploration.default_skin, init_state_name=exploration.init_state_name, states={ state_name: state.to_dict() for (state_name, state) in exploration.states.iteritems()}, param_specs=exploration.param_specs_dict, param_changes=exploration.param_change_dicts, ) model.commit(committer_id, commit_message, commit_cmds) event_services.ExplorationContentChangeEventHandler.record(exploration.id) exploration.version += 1 create_exploration_summary(exploration.id)
def _create_exploration( committer_id, exploration, commit_message, commit_cmds): """Ensures that rights for a new exploration are saved first. This is because _save_exploration() depends on the rights object being present to tell it whether to do strict validation or not. """ # This line is needed because otherwise a rights object will be created, # but the creation of an exploration object will fail. exploration.validate() rights_manager.create_new_exploration_rights(exploration.id, committer_id) model = exp_models.ExplorationModel( id=exploration.id, category=exploration.category, title=exploration.title, objective=exploration.objective, language_code=exploration.language_code, skill_tags=exploration.skill_tags, blurb=exploration.blurb, author_notes=exploration.author_notes, default_skin=exploration.default_skin, init_state_name=exploration.init_state_name, states={ state_name: state.to_dict() for (state_name, state) in exploration.states.iteritems()}, param_specs=exploration.param_specs_dict, param_changes=exploration.param_change_dicts, ) model.commit(committer_id, commit_message, commit_cmds) exploration.version += 1
def save_new_exp_with_states_schema_v0(self, exp_id, user_id, title): """Saves a new default exploration with a default version 0 states dictionary. This function should only be used for creating explorations in tests involving migration of datastore explorations that use an old states schema version. Note that it makes an explicit commit to the datastore instead of using the usual functions for updating and creating explorations. This is because the latter approach would result in an exploration with the *current* states schema version. """ exp_model = exp_models.ExplorationModel( id=exp_id, category="category", title=title, objective="Old objective", language_code="en", tags=[], blurb="", author_notes="", skin_customizations={"panels_contents": {}}, states_schema_version=0, init_state_name=feconf.DEFAULT_INIT_STATE_NAME, states=self.VERSION_0_STATES_DICT, param_specs={}, param_changes=[], ) rights_manager.create_new_exploration_rights(exp_id, user_id) commit_message = "New exploration created with title '%s'." % title exp_model.commit(user_id, commit_message, [{"cmd": "create_new", "title": "title", "category": "category"}])
def save_new_exp_with_states_schema_v34(self, exp_id, user_id, states_dict): """Saves a new default exploration with a default version 34 states dictionary. This function should only be used for creating explorations in tests involving migration of datastore explorations that use an old states schema version. Note that it makes an explicit commit to the datastore instead of using the usual functions for updating and creating explorations. This is because the latter approach would result in an exploration with the *current* states schema version. Args: exp_id: str. The exploration ID. user_id: str. The user_id of the creator. states_dict: dict. The dict representation of all the states. """ exp_model = exp_models.ExplorationModel( id=exp_id, category='category', title='title', objective='Old objective', language_code='en', tags=[], blurb='', author_notes='', states_schema_version=34, init_state_name=feconf.DEFAULT_INIT_STATE_NAME, states=states_dict, param_specs={}, param_changes=[] ) rights_manager.create_new_exploration_rights(exp_id, user_id) commit_message = 'New exploration created with title \'title\'.' exp_model.commit( user_id, commit_message, [{ 'cmd': 'create_new', 'title': 'title', 'category': 'category', }]) exp_rights = exp_models.ExplorationRightsModel.get_by_id(exp_id) exp_summary_model = exp_models.ExpSummaryModel( id=exp_id, title='title', category='category', objective='Old objective', language_code='en', tags=[], ratings=feconf.get_empty_ratings(), scaled_average_rating=feconf.EMPTY_SCALED_AVERAGE_RATING, status=exp_rights.status, community_owned=exp_rights.community_owned, owner_ids=exp_rights.owner_ids, contributor_ids=[], contributors_summary={}, ) exp_summary_model.update_timestamps() exp_summary_model.put()
def test_migration_job_skips_deleted_explorations(self): """Tests that the exploration migration job skips deleted explorations and does not attempt to migrate. """ # Save new default exploration with a default version 0 states # dictionary. exp_model = exp_models.ExplorationModel( id=self.NEW_EXP_ID, category='category', title='title', objective='', language_code='en', tags=[], blurb='', author_notes='', default_skin='conversation_v1', skin_customizations={'panels_contents': {}}, states_schema_version=0, init_state_name=feconf.DEFAULT_INIT_STATE_NAME, states=self.VERSION_0_STATES_DICT, param_specs={}, param_changes=[] ) rights_manager.create_new_exploration_rights( self.NEW_EXP_ID, self.ALBERT_ID) exp_model.commit(self.ALBERT_ID, 'old commit', [{ 'cmd': 'create_new', 'title': 'title', 'category': 'category', }]) # Note: This creates a summary based on the upgraded model (which is # fine). A summary is needed to delete the exploration. exp_services.create_exploration_summary(self.NEW_EXP_ID) # Delete the exploration before migration occurs. exp_services.delete_exploration(self.ALBERT_ID, self.NEW_EXP_ID) # Ensure the exploration is deleted. with self.assertRaisesRegexp(Exception, 'Entity .* not found'): exp_services.get_exploration_by_id(self.NEW_EXP_ID) # Start migration job on sample exploration. job_id = exp_jobs.ExplorationMigrationJobManager.create_new() exp_jobs.ExplorationMigrationJobManager.enqueue(job_id) # This running without errors indicates the deleted exploration is being # ignored, since otherwise exp_services.get_exploration_by_id (used # within the job) will raise an error. self.process_and_flush_pending_tasks() # Ensure the exploration is still deleted. with self.assertRaisesRegexp(Exception, 'Entity .* not found'): exp_services.get_exploration_by_id(self.NEW_EXP_ID)
def test_migration_job_does_not_have_validation_fail_on_default_exp(self): """Tests that the exploration migration job does not have a validation failure for a default exploration (of states schema version 0), due to the exploration having a null interaction ID in its initial state. """ # Save new default exploration with a default version 0 states # dictionary. This will be used to test default exploration migration, # which includes ensuring the job does not have a validation failure for # an exploration with a null interaction ID. exp_model = exp_models.ExplorationModel( id=self.NEW_EXP_ID, category='category', title='title', objective='', language_code='en', tags=[], blurb='', author_notes='', default_skin='conversation_v1', skin_customizations={'panels_contents': {}}, states_schema_version=0, init_state_name=feconf.DEFAULT_INIT_STATE_NAME, states=self.VERSION_0_STATES_DICT, param_specs={}, param_changes=[] ) rights_manager.create_new_exploration_rights( self.NEW_EXP_ID, self.ALBERT_ID) exp_model.commit(self.ALBERT_ID, 'old commit', [{ 'cmd': 'create_new', 'title': 'title', 'category': 'category', }]) # Start migration job on sample exploration. job_id = exp_jobs.ExplorationMigrationJobManager.create_new() exp_jobs.ExplorationMigrationJobManager.enqueue(job_id) self.process_and_flush_pending_tasks() # Verify the new exploration has been migrated by the job. updated_exp = exp_services.get_exploration_by_id(self.NEW_EXP_ID) self.assertEqual( updated_exp.states_schema_version, feconf.CURRENT_EXPLORATION_STATES_SCHEMA_VERSION) # Ensure the states structure within the exploration was changed. self.assertNotEqual( updated_exp.to_dict()['states'], self.VERSION_0_STATES_DICT)
def setUp(self): super(FeedbackThreadTests, self).setUp() self.signup(self.OWNER_EMAIL_1, self.OWNER_USERNAME_1) self.signup(self.OWNER_EMAIL_2, self.OWNER_USERNAME_2) self.signup(self.USER_EMAIL, self.USER_USERNAME) self.owner_id_1 = self.get_user_id_from_email(self.OWNER_EMAIL_1) self.owner_id_2 = self.get_user_id_from_email(self.OWNER_EMAIL_2) self.user_id = self.get_user_id_from_email(self.USER_EMAIL) self.owner_2 = user_services.UserActionsInfo(self.owner_id_2) # Create an exploration. self.save_new_valid_exploration( self.EXP_ID, self.owner_id_1, title=self.EXP_TITLE, category='Architecture', language_code='en') rights_manager.create_new_exploration_rights( self.EXP_ID, self.owner_id_2) rights_manager.publish_exploration(self.owner_2, self.EXP_ID)
def save_new_exp_with_states_schema_v0(self, exp_id, user_id, title): """Saves a new default exploration with a default version 0 states dictionary. This function should only be used for creating explorations in tests involving migration of datastore explorations that use an old states schema version. Note that it makes an explicit commit to the datastore instead of using the usual functions for updating and creating explorations. This is because the latter approach would result in an exploration with the *current* states schema version. """ exp_model = exp_models.ExplorationModel( id=exp_id, category='category', title=title, objective='Old objective', language_code='en', tags=[], blurb='', author_notes='', default_skin='conversation_v1', skin_customizations={'panels_contents': {}}, states_schema_version=0, init_state_name=feconf.DEFAULT_INIT_STATE_NAME, states=self.VERSION_0_STATES_DICT, param_specs={}, param_changes=[]) rights_manager.create_new_exploration_rights(exp_id, user_id) commit_message = 'New exploration created with title \'%s\'.' % title exp_model.commit(user_id, commit_message, [{ 'cmd': 'create_new', 'title': 'title', 'category': 'category', }])
def save_new_exp_with_states_schema_v0(self, exp_id, user_id, title): """Saves a new default exploration with a default version 0 states dictionary. This function should only be used for creating explorations in tests involving migration of datastore explorations that use an old states schema version. Note that it makes an explicit commit to the datastore instead of using the usual functions for updating and creating explorations. This is because the latter approach would result in an exploration with the *current* states schema version. Args: exp_id: str. The exploration ID. user_id: str. The user_id of the creator. title: str. The title of the exploration. """ exp_model = exp_models.ExplorationModel( id=exp_id, category='category', title=title, objective='Old objective', language_code='en', tags=[], blurb='', author_notes='', states_schema_version=0, init_state_name=feconf.DEFAULT_INIT_STATE_NAME, states=self.VERSION_0_STATES_DICT, param_specs={}, param_changes=[]) rights_manager.create_new_exploration_rights(exp_id, user_id) commit_message = 'New exploration created with title \'%s\'.' % title exp_model.commit(user_id, commit_message, [{ 'cmd': 'create_new', 'title': 'title', 'category': 'category', }]) exp_rights = exp_models.ExplorationRightsModel.get_by_id(exp_id) exp_summary_model = exp_models.ExpSummaryModel( id=exp_id, title=title, category='category', objective='Old objective', language_code='en', tags=[], ratings=feconf.get_empty_ratings(), scaled_average_rating=feconf.EMPTY_SCALED_AVERAGE_RATING, status=exp_rights.status, community_owned=exp_rights.community_owned, owner_ids=exp_rights.owner_ids, contributor_ids=[], contributors_summary={}, ) exp_summary_model.put() # Note: Also save state id mappping model for new exploration. If not # saved, it may cause errors in test cases. exploration = exp_services.get_exploration_from_model(exp_model) exp_services.create_and_save_state_id_mapping_model(exploration, [])
def test_migration_job_creates_appropriate_classifier_models(self): """Tests that the exploration migration job creates appropriate classifier data models for explorations. """ swap_states_schema_41 = self.swap(feconf, 'CURRENT_STATE_SCHEMA_VERSION', 41) swap_exp_schema_46 = self.swap(exp_domain.Exploration, 'CURRENT_EXP_SCHEMA_VERSION', 46) with swap_states_schema_41, swap_exp_schema_46: exp_model = exp_models.ExplorationModel( id=self.NEW_EXP_ID, category='category', title=self.EXP_TITLE, objective='Old objective', language_code='en', tags=[], blurb='', author_notes='', states_schema_version=41, init_state_name=feconf.DEFAULT_INIT_STATE_NAME, states={ 'END': { 'classifier_model_id': None, 'content': { 'content_id': 'content', 'html': 'Congratulations, you have finished!', }, 'interaction': { 'answer_groups': [], 'confirmed_unclassified_answers': [], 'customization_args': { 'recommendedExplorationIds': { 'value': [] }, }, 'default_outcome': None, 'hints': [], 'id': 'EndExploration', 'solution': None, }, 'next_content_id_index': 0, 'param_changes': [], 'recorded_voiceovers': { 'voiceovers_mapping': { 'content': {}, } }, 'solicit_answer_details': False, 'written_translations': { 'translations_mapping': { 'content': {}, } } }, 'Introduction': { 'classifier_model_id': None, 'content': { 'content_id': 'content', 'html': '' }, 'interaction': { 'answer_groups': [{ 'outcome': { 'dest': 'END', 'feedback': { 'content_id': 'feedback_1', 'html': '<p>Correct!</p>', }, 'labelled_as_correct': False, 'missing_prerequisite_skill_id': None, 'param_changes': [], 'refresher_exploration_id': None, }, 'rule_specs': [{ 'inputs': { 'x': { 'contentId': 'rule_input_3', 'normalizedStrSet': ['InputString'] } }, 'rule_type': 'Equals', }], 'tagged_skill_misconception_id': None, 'training_data': ['answer1', 'answer2', 'answer3'], }], 'confirmed_unclassified_answers': [], 'customization_args': { 'placeholder': { 'value': { 'content_id': 'ca_placeholder_2', 'unicode_str': '', }, }, 'rows': { 'value': 1 }, }, 'default_outcome': { 'dest': 'Introduction', 'feedback': { 'content_id': 'default_outcome', 'html': '' }, 'labelled_as_correct': False, 'missing_prerequisite_skill_id': None, 'param_changes': [], 'refresher_exploration_id': None, }, 'hints': [], 'id': 'TextInput', 'solution': None, }, 'next_content_id_index': 4, 'param_changes': [], 'recorded_voiceovers': { 'voiceovers_mapping': { 'ca_placeholder_2': {}, 'content': {}, 'default_outcome': {}, 'feedback_1': {}, 'rule_input_3': {}, } }, 'solicit_answer_details': False, 'written_translations': { 'translations_mapping': { 'ca_placeholder_2': {}, 'content': {}, 'default_outcome': {}, 'feedback_1': {}, 'rule_input_3': {}, } }, }, }, param_specs={}, param_changes=[]) rights_manager.create_new_exploration_rights( self.NEW_EXP_ID, self.albert_id) commit_message = ('New exploration created with title \'%s\'.' % self.EXP_TITLE) exp_model.commit(self.albert_id, commit_message, [{ 'cmd': 'create_new', 'title': 'title', 'category': 'category', }]) exp_rights = exp_models.ExplorationRightsModel.get_by_id( self.NEW_EXP_ID) exp_summary_model = exp_models.ExpSummaryModel( id=self.NEW_EXP_ID, title=self.EXP_TITLE, category='category', objective='Old objective', language_code='en', tags=[], ratings=feconf.get_empty_ratings(), scaled_average_rating=feconf.EMPTY_SCALED_AVERAGE_RATING, status=exp_rights.status, community_owned=exp_rights.community_owned, owner_ids=exp_rights.owner_ids, contributor_ids=[], contributors_summary={}) exp_summary_model.update_timestamps() exp_summary_model.put() exploration = exp_fetchers.get_exploration_by_id(self.NEW_EXP_ID) initial_state_name = list(exploration.states.keys())[0] # Store classifier model for the new exploration. classifier_model_id = ( classifier_models.ClassifierTrainingJobModel.create( 'TextClassifier', 'TextInput', self.NEW_EXP_ID, exploration.version, datetime.datetime.utcnow(), {}, initial_state_name, feconf.TRAINING_JOB_STATUS_COMPLETE, 1)) # Store training job model for the classifier model. classifier_models.StateTrainingJobsMappingModel.create( self.NEW_EXP_ID, exploration.version, initial_state_name, {'TextClassifier': classifier_model_id}) # Start migration job on sample exploration. job_id = exp_jobs_one_off.ExplorationMigrationJobManager.create_new() exp_jobs_one_off.ExplorationMigrationJobManager.enqueue(job_id) with self.swap(feconf, 'ENABLE_ML_CLASSIFIERS', True): with self.swap(feconf, 'MIN_TOTAL_TRAINING_EXAMPLES', 2): with self.swap(feconf, 'MIN_ASSIGNED_LABELS', 1): self.process_and_flush_pending_mapreduce_tasks() actual_output = ( exp_jobs_one_off.ExplorationMigrationJobManager.get_output(job_id)) expected_output = ['[u\'SUCCESS\', 1]'] self.assertEqual(actual_output, expected_output) new_exploration = exp_fetchers.get_exploration_by_id(self.NEW_EXP_ID) initial_state_name = list(new_exploration.states.keys())[0] self.assertLess(exploration.version, new_exploration.version) classifier_exp_mapping_model = ( classifier_models.StateTrainingJobsMappingModel.get_models( self.NEW_EXP_ID, new_exploration.version, [initial_state_name]))[0] self.assertEqual( classifier_exp_mapping_model. algorithm_ids_to_job_ids['TextClassifier'], classifier_model_id)