def get(self, comma_separated_skill_ids): """Handles GET requests.""" start_cursor = self.request.get('cursor') skill_ids = comma_separated_skill_ids.split(',') for skill_id in skill_ids: try: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception: raise self.PageNotFoundException(Exception('Invalid skill id')) try: skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) question_summaries, skill_descriptions_list, next_start_cursor = ( question_services.get_question_summaries_and_skill_descriptions( constants.NUM_QUESTIONS_PER_PAGE, skill_ids, start_cursor) ) return_dicts = [] for index, summary in enumerate(question_summaries): return_dicts.append({ 'summary': summary.to_dict(), 'skill_descriptions': skill_descriptions_list[index] }) self.values.update({ 'question_summary_dicts': return_dicts, 'next_start_cursor': next_start_cursor }) self.render_json(self.values)
def test_get_multi_skills(self): example_1 = skill_domain.WorkedExample( state_domain.SubtitledHtml('2', '<p>Example Question 1</p>'), state_domain.SubtitledHtml('3', '<p>Example Explanation 1</p>')) self.save_new_skill( 'skill_a', self.user_id_admin, description='Description A', misconceptions=[], skill_contents=skill_domain.SkillContents( 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': {} }}))) self.save_new_skill( 'skill_b', self.user_id_admin, description='Description B', misconceptions=[], skill_contents=skill_domain.SkillContents( 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': {} }}))) skills = skill_services.get_multi_skills(['skill_a', 'skill_b']) self.assertEqual(len(skills), 2) self.assertEqual(skills[0].id, 'skill_a') self.assertEqual(skills[0].description, 'Description A') self.assertEqual(skills[0].misconceptions, []) self.assertEqual(skills[1].id, 'skill_b') self.assertEqual(skills[1].description, 'Description B') self.assertEqual(skills[1].misconceptions, []) with self.assertRaisesRegexp(Exception, 'No skill exists for ID skill_c'): skill_services.get_multi_skills(['skill_a', 'skill_c'])
def get(self): """Handles GET requests.""" comma_separated_skill_ids = ( self.request.get('comma_separated_skill_ids')) if not comma_separated_skill_ids: raise self.InvalidInputException( 'Expected request to contain parameter ' 'comma_separated_skill_ids.') skill_ids = comma_separated_skill_ids.split(',') try: for skill_id in skill_ids: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception: raise self.InvalidInputException('Invalid skill ID %s' % skill_id) try: skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) degrees_of_mastery = skill_services.get_multi_user_skill_mastery( self.user_id, skill_ids) self.values.update({'degrees_of_mastery': degrees_of_mastery}) self.render_json(self.values)
def put(self): """Handles PUT requests.""" mastery_change_per_skill = ( self.payload.get('mastery_change_per_skill')) if (not mastery_change_per_skill or not isinstance(mastery_change_per_skill, dict)): raise self.InvalidInputException( 'Expected payload to contain mastery_change_per_skill ' 'as a dict.') skill_ids = list(mastery_change_per_skill.keys()) current_degrees_of_mastery = ( skill_services.get_multi_user_skill_mastery( self.user_id, skill_ids)) new_degrees_of_mastery = {} for skill_id in skill_ids: try: skill_domain.Skill.require_valid_skill_id(skill_id) except utils.ValidationError: raise self.InvalidInputException('Invalid skill ID %s' % skill_id) # float(bool) will not raise an error. if isinstance(mastery_change_per_skill[skill_id], bool): raise self.InvalidInputException( 'Expected degree of mastery of skill %s to be a number, ' 'received %s.' % (skill_id, mastery_change_per_skill[skill_id])) try: mastery_change_per_skill[skill_id] = (float( mastery_change_per_skill[skill_id])) except (TypeError, ValueError): raise self.InvalidInputException( 'Expected degree of mastery of skill %s to be a number, ' 'received %s.' % (skill_id, mastery_change_per_skill[skill_id])) if current_degrees_of_mastery[skill_id] is None: current_degrees_of_mastery[skill_id] = 0.0 new_degrees_of_mastery[skill_id] = ( current_degrees_of_mastery[skill_id] + mastery_change_per_skill[skill_id]) if new_degrees_of_mastery[skill_id] < 0.0: new_degrees_of_mastery[skill_id] = 0.0 elif new_degrees_of_mastery[skill_id] > 1.0: new_degrees_of_mastery[skill_id] = 1.0 try: skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) skill_services.create_multi_user_skill_mastery(self.user_id, new_degrees_of_mastery) self.render_json({})
def get(self, comma_separated_skill_ids): """Handles GET requests.""" start_cursor = self.request.get('cursor') skill_ids = comma_separated_skill_ids.split(',') for skill_id in skill_ids: try: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception: raise self.PageNotFoundException(Exception('Invalid skill id')) try: skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) ( question_summaries, merged_question_skill_links, next_start_cursor) = ( question_services.get_displayable_question_skill_link_details( constants.NUM_QUESTIONS_PER_PAGE, skill_ids, start_cursor) ) return_dicts = [] for index, summary in enumerate(question_summaries): if summary is not None: if len(skill_ids) == 1: return_dicts.append({ 'summary': summary.to_dict(), 'skill_id': merged_question_skill_links[ index].skill_ids[0], 'skill_description': ( merged_question_skill_links[ index].skill_descriptions[0]), 'skill_difficulty': ( merged_question_skill_links[ index].skill_difficulties[0]) }) else: return_dicts.append({ 'summary': summary.to_dict(), 'skill_ids': merged_question_skill_links[ index].skill_ids, 'skill_descriptions': ( merged_question_skill_links[ index].skill_descriptions), 'skill_difficulties': ( merged_question_skill_links[ index].skill_difficulties) }) self.values.update({ 'question_summary_dicts': return_dicts, 'next_start_cursor': next_start_cursor }) self.render_json(self.values)
def post(self, comma_separated_skill_ids): """Handles POST requests.""" skill_ids = comma_separated_skill_ids.split(',') try: for skill_id in skill_ids: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception: raise self.InvalidInputException try: skill_services.get_multi_skills(skill_ids) except Exception, e: raise self.PageNotFoundException(e)
def put(self): """Handles PUT requests.""" degree_of_mastery_per_skill = ( self.payload.get('degree_of_mastery_per_skill')) if (not degree_of_mastery_per_skill or not isinstance(degree_of_mastery_per_skill, dict)): raise self.InvalidInputException( 'Expected payload to contain degree_of_mastery_per_skill ' 'as a dict.') skill_ids = degree_of_mastery_per_skill.keys() for skill_id in skill_ids: try: skill_domain.Skill.require_valid_skill_id(skill_id) except utils.ValidationError: raise self.InvalidInputException('Invalid skill ID %s' % skill_id) # float(bool) will not raise an error. if isinstance(degree_of_mastery_per_skill[skill_id], bool): raise self.InvalidInputException( 'Expected degree of mastery of skill %s to be a number, ' 'received %s.' % (skill_id, degree_of_mastery_per_skill[skill_id])) try: degree_of_mastery_per_skill[skill_id] = (float( degree_of_mastery_per_skill[skill_id])) except (TypeError, ValueError): raise self.InvalidInputException( 'Expected degree of mastery of skill %s to be a number, ' 'received %s.' % (skill_id, degree_of_mastery_per_skill[skill_id])) if (degree_of_mastery_per_skill[skill_id] < 0.0 or degree_of_mastery_per_skill[skill_id] > 1.0): raise self.InvalidInputException( 'Expected degree of mastery of skill %s to be a float ' 'between 0.0 and 1.0, received %s.' % (skill_id, degree_of_mastery_per_skill[skill_id])) try: skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) skill_services.create_multi_user_skill_mastery( self.user_id, degree_of_mastery_per_skill) self.render_json({})
def test_get_multi_skills(self): self.save_new_skill('skill_a', self.user_id_admin, 'Description A', misconceptions=[], skill_contents=skill_domain.SkillContents( state_domain.SubtitledHtml('1', 'Explanation'), [state_domain.SubtitledHtml('2', 'Example 1')], { '1': {}, '2': {} }, state_domain.WrittenTranslations.from_dict({ 'translations_mapping': { '1': {}, '2': {} } }))) self.save_new_skill('skill_b', self.user_id_admin, 'Description B', misconceptions=[], skill_contents=skill_domain.SkillContents( state_domain.SubtitledHtml('1', 'Explanation'), [state_domain.SubtitledHtml('2', 'Example 1')], { '1': {}, '2': {} }, state_domain.WrittenTranslations.from_dict({ 'translations_mapping': { '1': {}, '2': {} } }))) skills = skill_services.get_multi_skills(['skill_a', 'skill_b']) self.assertEqual(len(skills), 2) self.assertEqual(skills[0].id, 'skill_a') self.assertEqual(skills[0].description, 'Description A') self.assertEqual(skills[0].misconceptions, []) self.assertEqual(skills[1].id, 'skill_b') self.assertEqual(skills[1].description, 'Description B') self.assertEqual(skills[1].misconceptions, []) with self.assertRaisesRegexp(Exception, 'No skill exists for ID skill_c'): skill_services.get_multi_skills(['skill_a', 'skill_c'])
def get(self, comma_separated_skill_ids): """Handles GET requests.""" start_cursor = self.request.get('cursor') skill_ids = comma_separated_skill_ids.split(',') for skill_id in skill_ids: try: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception: raise self.PageNotFoundException(Exception('Invalid skill id')) try: skill_services.get_multi_skills(skill_ids) except Exception, e: raise self.PageNotFoundException(e)
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.') associated_skill_ids = [ link.skill_id for link in (question_services. get_question_skill_links_of_question(question_id)) ] associated_skills = skill_services.get_multi_skills( associated_skill_ids) 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 _get_target_id_to_skill_opportunity_dict(suggestions): """Returns a dict of target_id to skill opportunity summary dict. Args: suggestions: list(BaseSuggestion). A list of suggestions to retrieve opportunity dicts. Returns: dict. Dict mapping target_id to corresponding skill opportunity dict. """ target_ids = set([s.target_id for s in suggestions]) opportunities = (opportunity_services.get_skill_opportunities_by_ids( list(target_ids))) opportunity_skill_ids = [opp.id for opp in opportunities] opportunity_id_to_skill = { skill.id: skill for skill in skill_services.get_multi_skills(opportunity_skill_ids) } opportunity_id_to_opportunity = {} for opp in opportunities: opp_dict = opp.to_dict() skill = opportunity_id_to_skill.get(opp.id) if skill is not None: opp_dict['skill_rubrics'] = [ rubric.to_dict() for rubric in skill.rubrics ] opportunity_id_to_opportunity[opp.id] = opp_dict return opportunity_id_to_opportunity
def get(self, topic_name): if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException # Topic cannot be None as an exception will be thrown from its decorator # if so. topic = topic_fetchers.get_topic_by_name(topic_name) comma_separated_subtopic_ids = self.request.get( 'selected_subtopic_ids') selected_subtopic_ids = comma_separated_subtopic_ids.split(',') selected_skill_ids = [] for subtopic in topic.subtopics: # An error is not thrown here, since it's fine to just ignore the # passed in subtopic IDs, if they don't exist, which would be the # case if the creator deletes subtopics after the learner has # loaded the topic viewer page. if python_utils.UNICODE(subtopic.id) in selected_subtopic_ids: selected_skill_ids.extend(subtopic.skill_ids) try: skills = skill_services.get_multi_skills(selected_skill_ids) except Exception as e: raise self.PageNotFoundException(e) skill_ids_to_descriptions_map = {} for skill in skills: skill_ids_to_descriptions_map[skill.id] = skill.description self.values.update({ 'topic_name': topic.name, 'skill_ids_to_descriptions_map': skill_ids_to_descriptions_map }) self.render_json(self.values)
def get(self, story_id): """Handles GET requests.""" if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException story = story_fetchers.get_story_by_id(story_id) latest_completed_node_ids = ( story_fetchers.get_latest_completed_node_ids( self.user_id, story_id)) if len(latest_completed_node_ids) == 0: raise self.PageNotFoundException try: skills = skill_services.get_multi_skills( story.get_acquired_skill_ids_for_node_ids( latest_completed_node_ids)) except Exception as e: raise self.PageNotFoundException(e) skill_descriptions = {} for skill in skills: skill_descriptions[skill.id] = skill.description self.values.update({ 'skill_descriptions': skill_descriptions, 'story_name': story.title }) self.render_json(self.values)
def test_get_multi_skills(self): self.save_new_skill('skill_a', self.user_id_admin, 'Description A', misconceptions=[], skill_contents=skill_domain.SkillContents( state_domain.SubtitledHtml('1', 'Explanation'), [state_domain.SubtitledHtml('2', 'Example 1')], { '1': {}, '2': {} }, state_domain.WrittenTranslations.from_dict({ 'translations_mapping': { '1': {}, '2': {} } }))) self.save_new_skill('skill_b', self.user_id_admin, 'Description B', misconceptions=[], skill_contents=skill_domain.SkillContents( state_domain.SubtitledHtml('1', 'Explanation'), [state_domain.SubtitledHtml('2', 'Example 1')], { '1': {}, '2': {} }, state_domain.WrittenTranslations.from_dict({ 'translations_mapping': { '1': {}, '2': {} } }))) try: skill_services.get_multi_skills(['skill_a', 'skill_b']) except Exception: self.fail(msg='Unexpected exception raised.') with self.assertRaisesRegexp(Exception, 'No skill exists for ID skill_c'): skill_services.get_multi_skills(['skill_a', 'skill_c'])
def get(self, topic_name): if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException # Topic cannot be None as an exception will be thrown from its decorator # if so. topic = topic_fetchers.get_topic_by_name(topic_name) try: skills = skill_services.get_multi_skills(topic.get_all_skill_ids()) except Exception, e: raise self.PageNotFoundException(e)
def get_skills_linked_to_question(question_id): """Returns a list of skills linked to a particular question. Args: question_id: str. ID of the question. Returns: list(Skill). The list of skills that are linked to the question. """ question = get_question_by_id(question_id) skills = skill_services.get_multi_skills(question.linked_skill_ids) return skills
def get_skills_linked_to_question(question_id): """Returns a list of skills linked to a particular question. Args: question_id: str. ID of the question. Returns: list(Skill). The list of skills that are linked to the question. """ question_skill_link_models = (question_models.QuestionSkillLinkModel. get_models_by_question_id(question_id)) skill_ids = [model.skill_id for model in question_skill_link_models] skills = skill_services.get_multi_skills(skill_ids) return skills
def get(self): """Returns all skill IDs linked to some topic.""" skill_ids = topic_services.get_all_skill_ids_assigned_to_some_topic() try: skills = skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) skill_dicts = [skill.to_dict() for skill in skills] self.values.update({'skills': skill_dicts}) 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_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 get(self, comma_separated_skill_ids): """Handles GET requests.""" if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException skill_ids = comma_separated_skill_ids.split(',') skills = skill_services.get_multi_skills(skill_ids) concept_card_dicts = [] for skill in skills: concept_card_dicts.append(skill.skill_contents.to_dict()) self.values.update({'concept_card_dicts': concept_card_dicts}) 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) 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 get(self, story_id): """Handles GET requests.""" if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException story = story_services.get_story_by_id(story_id) latest_completed_node_ids = ( story_services.get_latest_completed_node_ids( self.user_id, story_id)) if len(latest_completed_node_ids) == 0: raise self.PageNotFoundException try: skills = skill_services.get_multi_skills( story.get_acquired_skill_ids_for_node_ids( latest_completed_node_ids)) except Exception, e: raise self.PageNotFoundException(e)
def get(self, comma_separated_skill_ids): """Populates the data on skill pages of the skill ids.""" skill_ids = comma_separated_skill_ids.split(',') try: for skill_id in skill_ids: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception as e: raise self.PageNotFoundException('Invalid skill id.') try: skills = skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) skill_dicts = [skill.to_dict() for skill in skills] self.values.update({'skills': skill_dicts}) self.render_json(self.values)
class SkillDataHandler(base.BaseHandler): """A handler for accessing skills data.""" GET_HANDLER_ERROR_RETURN_TYPE = feconf.HANDLER_TYPE_JSON @acl_decorators.open_access def get(self, comma_separated_skill_ids): """Populates the data on skill pages of the skill ids.""" skill_ids = comma_separated_skill_ids.split(',') try: for skill_id in skill_ids: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception, e: raise self.PageNotFoundException('Invalid skill id.') try: skills = skill_services.get_multi_skills(skill_ids) except Exception, e: raise self.PageNotFoundException(e)
def get(self, topic_name): if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException # Topic cannot be None as an exception will be thrown from its decorator # if so. topic = topic_fetchers.get_topic_by_name(topic_name) try: skills = skill_services.get_multi_skills(topic.get_all_skill_ids()) except Exception as e: raise self.PageNotFoundException(e) skill_descriptions = {} for skill in skills: skill_descriptions[skill.id] = skill.description self.values.update({ 'topic_name': topic.name, 'skill_descriptions': skill_descriptions }) self.render_json(self.values)
def post(self): """Handles POST requests.""" skill_ids = self.payload.get('skill_ids') if not skill_ids: raise self.InvalidInputException( 'skill_ids parameter isn\'t present in the payload') if len(skill_ids) > constants.MAX_SKILLS_PER_QUESTION: raise self.InvalidInputException( 'More than %d QuestionSkillLinks for one question ' 'is not supported.' % constants.MAX_SKILLS_PER_QUESTION) try: for skill_id in skill_ids: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception as e: raise self.InvalidInputException('Skill ID(s) aren\'t valid: ', e) try: skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) question_dict = self.payload.get('question_dict') if ((question_dict['id'] is not None) or ('question_state_data' not in question_dict) or ('language_code' not in question_dict) or (question_dict['version'] != 0)): raise self.InvalidInputException( 'Question Data should contain id, state data, language code, ' + 'and its version should be set as 0') question_dict['question_state_data_schema_version'] = ( feconf.CURRENT_STATE_SCHEMA_VERSION) question_dict['id'] = question_services.get_new_question_id() question_dict['linked_skill_ids'] = skill_ids try: question = question_domain.Question.from_dict(question_dict) except Exception as e: raise self.InvalidInputException('Question structure is invalid:', e) skill_difficulties = self.payload.get('skill_difficulties') if not skill_difficulties: raise self.InvalidInputException( 'skill_difficulties not present in the payload') if len(skill_ids) != len(skill_difficulties): raise self.InvalidInputException( 'Skill difficulties don\'t match up with skill IDs') try: skill_difficulties = [ float(difficulty) for difficulty in skill_difficulties ] except (ValueError, TypeError): raise self.InvalidInputException( 'Skill difficulties must be a float value') if any((difficulty < 0 or difficulty > 1) for difficulty in skill_difficulties): raise self.InvalidInputException( 'Skill difficulties must be between 0 and 1') question_services.add_question(self.user_id, question) question_services.link_multiple_skills_for_question( self.user_id, question.id, skill_ids, skill_difficulties) html_list = question.question_state_data.get_all_html_content_strings() filenames = ( html_cleaner.get_image_filenames_from_html_strings(html_list)) image_validation_error_message_suffix = ( 'Please go to the question editor for question with id %s and edit ' 'the image.' % question.id) for filename in filenames: image = self.request.get(filename) if not image: logging.error( 'Image not provided for file with name %s when the question' ' with id %s was created.' % (filename, question.id)) raise self.InvalidInputException( 'No image data provided for file with name %s. %s' % (filename, image_validation_error_message_suffix)) try: file_format = ( image_validation_services.validate_image_and_filename( image, filename)) except utils.ValidationError as e: e = '%s %s' % (e, image_validation_error_message_suffix) raise self.InvalidInputException(e) image_is_compressible = (file_format in feconf.COMPRESSIBLE_IMAGE_FORMATS) fs_services.save_original_and_compressed_versions_of_image( filename, feconf.ENTITY_TYPE_QUESTION, question.id, image, 'image', image_is_compressible) self.values.update({'question_id': question.id}) self.render_json(self.values)
def post(self, story_id, node_id): if not constants.ENABLE_NEW_STRUCTURE_VIEWER_UPDATES: raise self.PageNotFoundException try: story_fetchers.get_node_index_by_story_id_and_node_id( story_id, node_id) except Exception as e: raise self.PageNotFoundException(e) story = story_fetchers.get_story_by_id(story_id) completed_nodes = story_fetchers.get_completed_nodes_in_story( self.user_id, story_id) completed_node_ids = [ completed_node.id for completed_node in completed_nodes ] ordered_nodes = [ node for node in story.story_contents.get_ordered_nodes() ] next_exp_ids = [] next_node_id = None if not node_id in completed_node_ids: story_services.record_completed_node_in_story_context( self.user_id, story_id, node_id) completed_nodes = story_fetchers.get_completed_nodes_in_story( self.user_id, story_id) completed_node_ids = [ completed_node.id for completed_node in completed_nodes ] for node in ordered_nodes: if node.id not in completed_node_ids: next_exp_ids = [node.exploration_id] next_node_id = node.id break ready_for_review_test = False exp_summaries = ( summary_services.get_displayable_exp_summary_dicts_matching_ids( next_exp_ids)) # If there are no questions for any of the acquired skills that the # learner has completed, do not show review tests. acquired_skills = skill_services.get_multi_skills( story.get_acquired_skill_ids_for_node_ids(completed_node_ids)) acquired_skill_ids = [skill.id for skill in acquired_skills] questions_available = len( question_services.get_questions_by_skill_ids( 1, acquired_skill_ids, False)) > 0 learner_completed_story = len(completed_nodes) == len(ordered_nodes) learner_at_review_point_in_story = ( len(exp_summaries) != 0 and (len(completed_nodes) & constants.NUM_EXPLORATIONS_PER_REVIEW_TEST == 0)) if questions_available and (learner_at_review_point_in_story or learner_completed_story): ready_for_review_test = True return self.render_json({ 'summaries': exp_summaries, 'ready_for_review_test': ready_for_review_test, 'next_node_id': next_node_id })
def post(self): """Handles POST requests.""" skill_ids = self.payload.get('skill_ids') if not skill_ids: raise self.InvalidInputException( 'skill_ids parameter isn\'t present in the payload') if len(skill_ids) > constants.MAX_SKILLS_PER_QUESTION: raise self.InvalidInputException( 'More than %d QuestionSkillLinks for one question ' 'is not supported.' % constants.MAX_SKILLS_PER_QUESTION) try: for skill_id in skill_ids: skill_domain.Skill.require_valid_skill_id(skill_id) except Exception as e: raise self.InvalidInputException('Skill ID(s) aren\'t valid: ', e) try: skill_services.get_multi_skills(skill_ids) except Exception as e: raise self.PageNotFoundException(e) question_dict = self.payload.get('question_dict') if ((question_dict['id'] is not None) or ('question_state_data' not in question_dict) or ('language_code' not in question_dict) or (question_dict['version'] != 1)): raise self.InvalidInputException( 'Question Data should contain id, state data, language code, ' + 'and its version should be set as 1') question_dict['question_state_data_schema_version'] = ( feconf.CURRENT_STATE_SCHEMA_VERSION) question_dict['id'] = question_services.get_new_question_id() question_dict['linked_skill_ids'] = skill_ids try: question = question_domain.Question.from_dict(question_dict) except Exception as e: raise self.InvalidInputException('Question structure is invalid:', e) skill_difficulties = self.payload.get('skill_difficulties') if not skill_difficulties: raise self.InvalidInputException( 'skill_difficulties not present in the payload') if len(skill_ids) != len(skill_difficulties): raise self.InvalidInputException( 'Skill difficulties don\'t match up with skill IDs') try: skill_difficulties = [ float(difficulty) for difficulty in skill_difficulties ] except (ValueError, TypeError): raise self.InvalidInputException( 'Skill difficulties must be a float value') if any((difficulty < 0 or difficulty > 1) for difficulty in skill_difficulties): raise self.InvalidInputException( 'Skill difficulties must be between 0 and 1') question_services.add_question(self.user_id, question) question_services.link_multiple_skills_for_question( self.user_id, question.id, skill_ids, skill_difficulties) self.values.update({'question_id': question.id}) self.render_json(self.values)