def test_get_question_summaries_for_collection(self): """Tests to verify get_question_summaries_for_collection method.""" coll_id_0 = '0_collection_id' exp_id_0 = '0_exploration_id' self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) # Create a new collection and exploration. self.save_new_valid_collection(coll_id_0, self.owner_id, exploration_id=exp_id_0) # Add a skill. collection_services.update_collection( self.owner_id, coll_id_0, [{ 'cmd': collection_domain.CMD_ADD_COLLECTION_SKILL, 'name': 'skill0' }], 'Add a new skill') collection = collection_services.get_collection_by_id(coll_id_0) skill_id = collection.get_skill_id_from_skill_name('skill0') collection_node = collection.get_node(exp_id_0) collection_node.update_acquired_skill_ids([skill_id]) # Update the acquired skill IDs for the exploration. collection_services.update_collection( self.owner_id, coll_id_0, [{ 'cmd': collection_domain.CMD_EDIT_COLLECTION_NODE_PROPERTY, 'property_name': collection_domain.COLLECTION_NODE_PROPERTY_ACQUIRED_SKILL_IDS, # pylint: disable=line-too-long 'exploration_id': exp_id_0, 'new_value': [skill_id] }], 'Update skill') question = question_domain.Question( 'dummy', 'A Question', exp_domain.State.create_default_state('ABC').to_dict(), 1, coll_id_0, 'en') question_id = question_services.add_question(self.owner_id, question) question = question_services.get_question_by_id(question_id) question_services.add_question_id_to_skill(question.question_id, coll_id_0, skill_id, self.owner_id) question_summaries = ( question_services.get_question_summaries_for_collection(coll_id_0)) self.assertEqual(question_summaries[0].question_id, question_id) self.assertEqual(question_summaries[0].question_title, question.title) self.assertEqual(question_summaries[0].skill_names, ['skill0'])
def get(self, entity_type, entity_id): """Handles the GET requests for learner answer info for an exploration state. """ if not constants.ENABLE_SOLICIT_ANSWER_DETAILS_FEATURE: raise self.PageNotFoundException learner_answer_info_data = [] if entity_type == feconf.ENTITY_TYPE_EXPLORATION: exp = exp_fetchers.get_exploration_by_id(entity_id) for state_name in exp.states: state_reference = ( stats_services.get_state_reference_for_exploration( entity_id, state_name)) learner_answer_details = ( stats_services.get_learner_answer_details( feconf.ENTITY_TYPE_EXPLORATION, state_reference)) if learner_answer_details is not None: learner_answer_info_data.append({ 'state_name': state_name, 'interaction_id': learner_answer_details.interaction_id, 'customization_args': exp.states[state_name].interaction .to_dict()['customization_args'], 'learner_answer_info_dicts': [ learner_answer_info.to_dict() for learner_answer_info in learner_answer_details.learner_answer_info_list] }) elif entity_type == feconf.ENTITY_TYPE_QUESTION: question = question_services.get_question_by_id(entity_id) state_reference = stats_services.get_state_reference_for_question( entity_id) learner_answer_details = stats_services.get_learner_answer_details( feconf.ENTITY_TYPE_QUESTION, state_reference) if learner_answer_details is not None: learner_answer_info_dicts = [ learner_answer_info.to_dict() for learner_answer_info in learner_answer_details.learner_answer_info_list] learner_answer_info_data = { 'interaction_id': learner_answer_details.interaction_id, 'customization_args': question.question_state_data.interaction .to_dict()['customization_args'], 'learner_answer_info_dicts': learner_answer_info_dicts } self.render_json({ 'learner_answer_info_data': learner_answer_info_data })
def get_state_reference_for_question(question_id): """Returns the generated state reference for the given question id. Args: question_id: str. ID of the question. Returns: str. The generated state reference. """ question = question_services.get_question_by_id(question_id, strict=False) if question is None: raise utils.InvalidInputException( 'No question with the given question id exists.') return (stats_models.LearnerAnswerDetailsModel. get_state_reference_for_question(question_id))
def test_deleted_question_are_not_processed(self): """Tests that the job does not process deleted questions.""" # Delete the question before migration occurs. question_services.delete_question(self.albert_id, self.QUESTION_ID) # Ensure the question is deleted. with self.assertRaisesRegexp(Exception, 'Entity .* not found'): question_services.get_question_by_id(self.QUESTION_ID) # Start migration job on sample question. job_id = (question_jobs_one_off.FixQuestionImagesStorageOneOffJob. create_new()) question_jobs_one_off.FixQuestionImagesStorageOneOffJob.enqueue(job_id) self.process_and_flush_pending_mapreduce_tasks() # Ensure that the question is still deleted and the output is None. with self.assertRaisesRegexp(Exception, 'Entity .* not found'): question_services.get_question_by_id(self.QUESTION_ID) output = (question_jobs_one_off.FixQuestionImagesStorageOneOffJob. get_output(job_id)) self.assertEqual( [[u'question_deleted', [u'Encountered 1 deleted questions.']]], [ast.literal_eval(x) for x in output])
def get(self, question_id): """Gets the data for the question overview page.""" 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.') self.values.update({'question_dict': question.to_dict()}) self.render_json(self.values)
def get(self, question_id): """Gets the data for the question overview page.""" question = question_services.get_question_by_id(question_id, strict=False) associated_skill_dicts = [ skill.to_dict() for skill in skill_fetchers.get_multi_skills( question.linked_skill_ids) ] self.values.update({ 'question_dict': question.to_dict(), 'associated_skill_dicts': associated_skill_dicts }) self.render_json(self.values)
def test_audit_job_skips_deleted_question(self): """Tests that the snapshot migration audit job skips deleted questions and does not attempt to migrate. """ self.save_new_question( self.QUESTION_ID, self.albert_id, self._create_valid_question_data('ABC'), [self.skill_id]) # Delete the question before migration occurs. question_services.delete_question(self.albert_id, self.QUESTION_ID) # Ensure the question is deleted. with self.assertRaisesRegexp(Exception, 'Entity .* not found'): question_services.get_question_by_id(self.QUESTION_ID) # Start migration job on sample question. job_id = ( question_jobs_one_off.QuestionSnapshotsMigrationAuditJob. create_new()) question_jobs_one_off.QuestionSnapshotsMigrationAuditJob.enqueue(job_id) # This running without errors indicates the deleted question is # being ignored. self.process_and_flush_pending_mapreduce_tasks() actual_output = ( question_jobs_one_off.QuestionSnapshotsMigrationAuditJob.get_output( job_id)) expected_output_choices = [ '[u\'INFO - Question does not exist\', [u\'%s-1\', u\'%s-2\']]' % (self.QUESTION_ID, self.QUESTION_ID), '[u\'INFO - Exploration does not exist\', [u\'%s-2\', u\'%s-1\']]' % (self.QUESTION_ID, self.QUESTION_ID) ] self.assertEqual(len(actual_output), 1) self.assertIn(actual_output[0], expected_output_choices)
def map(item): question_id = item.get_unversioned_instance_id() latest_question = question_services.get_question_by_id(question_id, strict=False) if latest_question is None: yield ('INFO - Question does not exist', item.id) return question_model = question_models.QuestionModel.get(question_id) if (question_model.question_state_data_schema_version != feconf.CURRENT_STATE_SCHEMA_VERSION): yield ('FAILURE - Question is not at latest schema version', question_id) return try: latest_question.validate() except Exception as e: yield ('INFO - Question %s failed validation' % item.id, e) target_state_schema_version = feconf.CURRENT_STATE_SCHEMA_VERSION current_state_schema_version = item.content[ 'question_state_data_schema_version'] if current_state_schema_version == target_state_schema_version: yield ('SUCCESS - Snapshot is already at latest schema version', item.id) return versioned_question_state = { 'states_schema_version': current_state_schema_version, 'state': item.content['question_state_data'] } while current_state_schema_version < target_state_schema_version: try: question_domain.Question.update_state_from_model( versioned_question_state, current_state_schema_version) current_state_schema_version += 1 except Exception as e: error_message = ( 'Question snapshot %s failed migration to state ' 'v%s: %s' % (item.id, current_state_schema_version + 1, e)) logging.exception(error_message) yield ('MIGRATION_ERROR', error_message.encode('utf-8')) break if target_state_schema_version == current_state_schema_version: yield ('SUCCESS', 1)
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 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_delete_question_skill_link(self): question_services.create_new_question_skill_link( self.editor_id, self.question_id, 'skill_1', 0.3) question_services.create_new_question_skill_link( self.editor_id, self.question_id, 'skill_2', 0.3) question_services.delete_question_skill_link( self.editor_id, self.question_id, 'skill_1') skill_ids = [skill.id for skill in question_services.get_skills_linked_to_question( self.question_id)] self.assertItemsEqual(skill_ids, ['skill_2']) question_services.delete_question_skill_link( self.editor_id, self.question_id, 'skill_2') question = question_services.get_question_by_id( self.question_id, strict=False) self.assertIsNone(question)
def get(self, question_id): """Gets the data for the question overview page.""" 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.') associated_skill_dicts = [ skill.to_dict() for skill in skill_services.get_multi_skills( question.linked_skill_ids) ] self.values.update({ 'question_dict': question.to_dict(), 'associated_skill_dicts': associated_skill_dicts }) self.render_json(self.values)
def test_batch_get(self): """Tests get method of questions batch handler.""" question_id = question_services.add_question( self.owner_id, self.question) self.question = question_services.get_question_by_id(question_id) question_services.add_question_id_to_skill( self.question.question_id, self.question.collection_id, self.skill_id, self.owner_id) collection_services.record_played_exploration_in_collection_context( self.new_user_id, self.collection_id, self.exp_id) payload = {} payload['collection_id'] = self.collection_id payload['stringified_skill_ids'] = json.dumps( [self.skill_id, 'test']) self.login(self.NEW_USER_EMAIL) response_json = self.get_json( '%s/batch' % feconf.QUESTION_DATA_URL, payload, expect_errors=False) self.assertIn(self.question.to_dict(), response_json['questions_dict']) self.assertEqual(len(response_json['questions_dict']), 1) response = self.testapp.get( '%s/batch' % feconf.QUESTION_DATA_URL, expect_errors=True) self.assertEqual(response.status_int, 404) del payload['stringified_skill_ids'] response = self.testapp.get( '%s/batch' % feconf.QUESTION_DATA_URL, payload, expect_errors=True) self.assertEqual(response.status_int, 404) self.logout() self.login(self.random_email) response_json = self.testapp.get( '%s/batch' % feconf.QUESTION_DATA_URL, payload, expect_errors=True) self.assertEqual(response.status_int, 404)
def test_manager_get(self): """Tests get method of question manager handler.""" question_id = question_services.add_question( self.owner_id, self.question) self.question = question_services.get_question_by_id(question_id) question_services.add_question_id_to_skill( self.question.question_id, self.question.collection_id, self.skill_id, self.owner_id) self.login(self.NEW_USER_EMAIL) payload = {} payload['collection_id'] = self.collection_id response_json = self.get_json( '%s' % feconf.QUESTION_MANAGER_URL, payload, expect_errors=False) expected_question_summary = question_domain.QuestionSummary( self.question.question_id, self.question.title, ['test']) self.assertIn( expected_question_summary.to_dict(), response_json['question_summary_dicts']) response = self.testapp.get( '%s/batch' % feconf.QUESTION_MANAGER_URL, expect_errors=True) self.assertEqual(response.status_int, 404) del payload['collection_id'] response = self.testapp.get( '%s/batch' % feconf.QUESTION_MANAGER_URL, payload, expect_errors=True) self.assertEqual(response.status_int, 404) self.logout() self.login(self.random_email) payload['collection_id'] = self.collection_id response = self.testapp.get( '%s' % feconf.QUESTION_MANAGER_URL, payload, expect_errors=True) self.assertEqual(response.status_int, 401)
def get(self, question_id): """Gets the data for the question overview page.""" if not constants.ENABLE_NEW_STRUCTURE_EDITORS: 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.') associated_skills = question_services.get_skills_linked_to_question( question_id) associated_skill_dicts = [ skill.to_dict() for skill in associated_skills] self.values.update({ 'question_dict': question.to_dict(), 'associated_skill_dicts': associated_skill_dicts }) self.render_json(self.values)
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 map(item): if item.deleted: return question = question_services.get_question_by_id(item.id) html_string = ''.join( question.question_state_data.get_all_html_content_strings()) list_of_latex_strings_without_svg = ( html_validation_service.get_latex_strings_without_svg_from_html( html_string)) latex_string_to_filename_mapping = ( html_validation_service. extract_svg_filename_latex_mapping_in_math_rte_components( html_string)) if len(latex_string_to_filename_mapping) > 0: latex_strings_with_svg = [ latex_string_to_filename[1] for latex_string_to_filename in ( latex_string_to_filename_mapping) ] yield (QuestionsMathRteAuditOneOffJob._LATEX_STRINGS_HAVING_SVG, (item.id, latex_strings_with_svg)) if len(list_of_latex_strings_without_svg) > 0: yield (QuestionsMathRteAuditOneOffJob._LATEX_STRINGS_WITHOUT_SVG, (item.id, list_of_latex_strings_without_svg))