def get_exploration_from_model(exploration_model, run_conversion=True): """Returns an Exploration domain object given an exploration model loaded from the datastore. If run_conversion is True, then the exploration's states schema version will be checked against the current states schema version. If they do not match, the exploration will be automatically updated to the latest states schema version. IMPORTANT NOTE TO DEVELOPERS: In general, run_conversion should never be False. This option is only used for testing that the states schema version migration works correctly, and it should never be changed otherwise. Args: exploration_model: ExplorationModel. An exploration storage model. run_conversion: bool. When True, updates the exploration to the latest states_schema_version if necessary. Returns: Exploration. The exploration domain object corresponding to the given exploration model. """ # Ensure the original exploration model does not get altered. versioned_exploration_states = { 'states_schema_version': exploration_model.states_schema_version, 'states': copy.deepcopy(exploration_model.states) } init_state_name = exploration_model.init_state_name # If the exploration uses the latest states schema version, no conversion # is necessary. if (run_conversion and exploration_model.states_schema_version != feconf.CURRENT_STATE_SCHEMA_VERSION): _migrate_states_schema(versioned_exploration_states, init_state_name) return exp_domain.Exploration( exploration_model.id, exploration_model.title, exploration_model.category, exploration_model.objective, exploration_model.language_code, exploration_model.tags, exploration_model.blurb, exploration_model.author_notes, versioned_exploration_states['states_schema_version'], exploration_model.init_state_name, versioned_exploration_states['states'], exploration_model.param_specs, exploration_model.param_changes, exploration_model.version, exploration_model.auto_tts_enabled, exploration_model.correctness_feedback_enabled, exploration_model.edits_allowed, created_on=exploration_model.created_on, last_updated=exploration_model.last_updated)
def get_exploration_from_model(exploration_model): return exp_domain.Exploration( exploration_model.id, exploration_model.title, exploration_model.category, exploration_model.objective, exploration_model.language_code, exploration_model.skill_tags, exploration_model.blurb, exploration_model.author_notes, exploration_model.default_skin, exploration_model.init_state_name, exploration_model.states, exploration_model.param_specs, exploration_model.param_changes, exploration_model.version, exploration_model.created_on, exploration_model.last_updated)
def test_skip_migration_for_suggestion_with_new_math_schema(self): """Tests that a suggestion already having the new math_schema is not migrated. """ expected_html_content = ( '<p>Value</p><oppia-noninteractive-math math_content-with-value=' '"{&quot;raw_latex&quot;: &quot;+,-,-,+&quot;, &' 'amp;quot;svg_filename&quot;: &quot;&quot;}"></oppia' '-noninteractive-math>') state_dict = { 'classifier_model_id': None, 'content': { 'content_id': 'content', 'html': expected_html_content }, 'interaction': { 'answer_groups': [], 'confirmed_unclassified_answers': [], 'customization_args': {}, 'default_outcome': { 'dest': 'Introduction', 'feedback': { 'content_id': 'default_outcome', 'html': expected_html_content }, 'labelled_as_correct': False, 'param_changes': [], 'refresher_exploration_id': None, 'missing_prerequisite_skill_id': None }, 'hints': [], 'id': None, 'solution': None, }, 'param_changes': [], 'recorded_voiceovers': { 'voiceovers_mapping': { 'content': {}, 'default_outcome': {} } }, 'solicit_answer_details': False, 'written_translations': { 'translations_mapping': { 'content': {}, 'default_outcome': {} } } } states = { 'Introduction': state_dict } exploration = ( exp_domain.Exploration( 'exp1', feconf.DEFAULT_EXPLORATION_TITLE, 'Algebra', feconf.DEFAULT_EXPLORATION_OBJECTIVE, constants.DEFAULT_LANGUAGE_CODE, [], '', '', feconf.CURRENT_STATE_SCHEMA_VERSION, feconf.DEFAULT_INIT_STATE_NAME, states, {}, [], 0, False, False)) exp_services.save_new_exploration(self.author_id, exploration) change_dict = { 'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY, 'property_name': exp_domain.STATE_PROPERTY_CONTENT, 'state_name': 'Introduction', 'new_value': { 'content_id': 'content', 'html': 'new suggestion' }, 'old_value': { 'content_id': 'content', 'html': expected_html_content } } with self.swap( feedback_models.GeneralFeedbackThreadModel, 'generate_new_thread_id', self.mock_generate_new_exploration_thread_id): suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, self.target_id, self.target_version_at_submission, self.author_id, change_dict, 'test description') job_id = ( suggestion_jobs_one_off. SuggestionMathMigrationOneOffJob.create_new()) suggestion_jobs_one_off.SuggestionMathMigrationOneOffJob.enqueue(job_id) self.process_and_flush_pending_tasks() actual_output = ( suggestion_jobs_one_off. SuggestionMathMigrationOneOffJob.get_output(job_id)) self.assertEqual(len(actual_output), 0)
def test_for_html_in_suggestions_with_math_rte(self): """Checks that correct number of hints are tabulated when there is single exploration. """ html_content = ( '<p>Value</p><oppia-noninteractive-math math_content-with-value=' '"{&quot;raw_latex&quot;: &quot;+,-,-,+&quot;, &' 'amp;quot;svg_filename&quot;: &quot;&quot;}"></oppia' '-noninteractive-math>') state_dict = { 'classifier_model_id': None, 'content': { 'content_id': 'content', 'html': html_content }, 'interaction': { 'answer_groups': [], 'confirmed_unclassified_answers': [], 'customization_args': {}, 'default_outcome': { 'dest': 'Introduction', 'feedback': { 'content_id': 'default_outcome', 'html': html_content }, 'labelled_as_correct': False, 'param_changes': [], 'refresher_exploration_id': None, 'missing_prerequisite_skill_id': None }, 'hints': [], 'id': None, 'solution': None, }, 'param_changes': [], 'recorded_voiceovers': { 'voiceovers_mapping': { 'content': {}, 'default_outcome': {} } }, 'solicit_answer_details': False, 'written_translations': { 'translations_mapping': { 'content': {}, 'default_outcome': {} } } } states = { 'Introduction': state_dict } exploration = ( exp_domain.Exploration( 'exp1', feconf.DEFAULT_EXPLORATION_TITLE, feconf.DEFAULT_EXPLORATION_CATEGORY, feconf.DEFAULT_EXPLORATION_OBJECTIVE, constants.DEFAULT_LANGUAGE_CODE, [], '', '', feconf.CURRENT_STATE_SCHEMA_VERSION, feconf.DEFAULT_INIT_STATE_NAME, states, {}, [], 0, False, False)) exp_services.save_new_exploration(self.author_id, exploration) add_translation_change_dict = { 'cmd': 'add_translation', 'state_name': 'Introduction', 'content_id': 'content', 'language_code': 'hi', 'content_html': html_content, 'translation_html': html_content } with self.swap( feedback_models.GeneralFeedbackThreadModel, 'generate_new_thread_id', self.mock_generate_new_exploration_thread_id): suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_TRANSLATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, self.target_id, self.target_version_at_submission, self.author_id, add_translation_change_dict, 'test description') answer_group = { 'outcome': { 'dest': None, 'feedback': { 'content_id': 'feedback_1', 'html': html_content }, 'labelled_as_correct': True, 'param_changes': [], 'refresher_exploration_id': None, 'missing_prerequisite_skill_id': None }, 'rule_specs': [{ 'inputs': { 'x': 0 }, 'rule_type': 'Equals' }], 'training_data': [], 'tagged_skill_misconception_id': None } question_state_dict_with_math = { 'content': { 'content_id': 'content_1', 'html': 'Question 1' }, 'recorded_voiceovers': { 'voiceovers_mapping': { 'content_1': {}, 'feedback_1': {}, 'feedback_2': {}, 'hint_1': {}, 'solution': {} } }, 'written_translations': { 'translations_mapping': { 'content_1': {}, 'feedback_1': {}, 'feedback_2': {}, 'hint_1': {}, 'solution': { 'en': { 'html': html_content, 'needs_update': True } } } }, 'interaction': { 'answer_groups': [answer_group], 'confirmed_unclassified_answers': [], 'customization_args': { 'choices': { 'value': [html_content] }, 'showChoicesInShuffledOrder': { 'value': True } }, 'default_outcome': { 'dest': None, 'feedback': { 'content_id': 'feedback_2', 'html': html_content }, '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': { 'answer_is_exclusive': False, 'correct_answer': 0, 'explanation': { 'content_id': 'solution', 'html': '<p>This is a solution.</p>' } }, 'id': 'MultipleChoiceInput' }, 'param_changes': [], 'solicit_answer_details': False, 'classifier_model_id': None } suggestion_dict_with_math = { 'suggestion_id': 'skill2.thread1', 'suggestion_type': suggestion_models.SUGGESTION_TYPE_ADD_QUESTION, 'target_type': suggestion_models.TARGET_TYPE_SKILL, 'target_id': 'skill2', 'target_version_at_submission': 1, 'status': suggestion_models.STATUS_ACCEPTED, 'author_name': 'author', 'final_reviewer_id': self.reviewer_id, 'change': { 'cmd': question_domain.CMD_CREATE_NEW_FULLY_SPECIFIED_QUESTION, 'question_dict': { 'question_state_data': question_state_dict_with_math, 'language_code': 'en', 'question_state_data_schema_version': ( feconf.CURRENT_STATE_SCHEMA_VERSION), 'linked_skill_ids': ['skill_2'] }, 'skill_id': 'skill_2', 'skill_difficulty': 0.3, }, 'score_category': 'question.skill1', 'last_updated': utils.get_time_in_millisecs(self.fake_date) } with self.swap( feedback_models.GeneralFeedbackThreadModel, 'generate_new_thread_id', self.mock_generate_new_skill_thread_id): suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_ADD_QUESTION, suggestion_models.TARGET_TYPE_SKILL, 'skill1', feconf.CURRENT_STATE_SCHEMA_VERSION, self.author_id, suggestion_dict_with_math['change'], 'test description') job_id = ( suggestion_jobs_one_off. SuggestionMathRteAuditOneOffJob.create_new()) suggestion_jobs_one_off.SuggestionMathRteAuditOneOffJob.enqueue(job_id) self.process_and_flush_pending_tasks() actual_output = ( suggestion_jobs_one_off. SuggestionMathRteAuditOneOffJob.get_output(job_id)) self.assertRegexpMatches( python_utils.UNICODE(actual_output), '2 suggestions have Math components in them')
def test_for_html_in_suggestion_edit_content_with_math_rte(self): """Checks that correct number of hints are tabulated when there is single exploration. """ html_content = ( '<p>Value</p><oppia-noninteractive-math raw_latex-with-value="&a' 'mp;quot;+,-,-,+&quot;"></oppia-noninteractive-math>') state_dict = { 'classifier_model_id': None, 'content': { 'content_id': 'content', 'html': html_content }, 'interaction': { 'answer_groups': [], 'confirmed_unclassified_answers': [], 'customization_args': {}, 'default_outcome': { 'dest': 'Introduction', 'feedback': { 'content_id': 'default_outcome', 'html': html_content }, 'labelled_as_correct': False, 'param_changes': [], 'refresher_exploration_id': None, 'missing_prerequisite_skill_id': None }, 'hints': [], 'id': None, 'solution': None, }, 'param_changes': [], 'recorded_voiceovers': { 'voiceovers_mapping': { 'content': {}, 'default_outcome': {} } }, 'solicit_answer_details': False, 'written_translations': { 'translations_mapping': { 'content': {}, 'default_outcome': {} } } } states = { 'Introduction': state_dict } exploration = ( exp_domain.Exploration( 'exp1', feconf.DEFAULT_EXPLORATION_TITLE, feconf.DEFAULT_EXPLORATION_CATEGORY, feconf.DEFAULT_EXPLORATION_OBJECTIVE, constants.DEFAULT_LANGUAGE_CODE, [], '', '', feconf.CURRENT_STATE_SCHEMA_VERSION, feconf.DEFAULT_INIT_STATE_NAME, states, {}, [], 0, False, False)) exp_services.save_new_exploration(self.author_id, exploration) change_dict = { 'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY, 'property_name': exp_domain.STATE_PROPERTY_CONTENT, 'state_name': 'Introduction', 'new_value': { 'content_id': 'content', 'html': 'suggestion content' }, 'old_value': { 'content_id': 'content', 'html': html_content } } with self.swap( feedback_models.GeneralFeedbackThreadModel, 'generate_new_thread_id', self.mock_generate_new_exploration_thread_id): suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, self.target_id, self.target_version_at_submission, self.author_id, change_dict, 'test description') job_id = ( suggestion_jobs_one_off. SuggestionMathRteAuditOneOffJob.create_new()) suggestion_jobs_one_off.SuggestionMathRteAuditOneOffJob.enqueue(job_id) self.process_and_flush_pending_tasks() actual_output = ( suggestion_jobs_one_off. SuggestionMathRteAuditOneOffJob.get_output(job_id)) self.assertRegexpMatches( python_utils.UNICODE(actual_output), '1 suggestions have Math components in them')