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 ((len(exp_summaries) != 0 and len(completed_nodes) % constants.NUM_EXPLORATIONS_PER_REVIEW_TEST == 0) or (len(completed_nodes) == len(ordered_nodes))): 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 _record_node_completion(self, story_id, node_id, completed_node_ids, ordered_nodes): """Records node completion.""" 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) next_exp_ids = [] next_node_id = None if node_id not 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 return (next_exp_ids, next_node_id, completed_node_ids)
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) completed_node_ids = [ completed_node.id for completed_node in story_fetchers.get_completed_nodes_in_story(self.user_id, story_id)] ordered_node_dicts = [ node.to_dict() for node in story.story_contents.get_ordered_nodes() ] for node in ordered_node_dicts: node['completed'] = False if node['id'] in completed_node_ids: node['completed'] = True exp_ids = [ node['exploration_id'] for node in ordered_node_dicts] exp_summary_dicts = ( summary_services.get_displayable_exp_summary_dicts_matching_ids( exp_ids, user=self.user)) for ind, node in enumerate(ordered_node_dicts): node['exp_summary_dict'] = exp_summary_dicts[ind] self.values.update({ 'story_title': story.title, 'story_description': story.description, 'story_nodes': ordered_node_dicts }) self.render_json(self.values)
def test_get_completed_nodes_in_story(self): self._record_completion(self.owner_id, self.STORY_1_ID, self.NODE_ID_1) self._record_completion(self.owner_id, self.STORY_1_ID, self.NODE_ID_2) for ind, completed_node in enumerate( story_fetchers.get_completed_nodes_in_story( self.owner_id, self.STORY_1_ID)): self.assertEqual(completed_node.to_dict(), self.nodes[ind].to_dict())
def test_get_completed_nodes_in_story(self): story = story_fetchers.get_story_by_id(self.STORY_ID) story_services.record_completed_node_in_story_context( self.USER_ID, self.STORY_ID, self.NODE_ID_1) story_services.record_completed_node_in_story_context( self.USER_ID, self.STORY_ID, self.NODE_ID_2) for ind, completed_node in enumerate( story_fetchers.get_completed_nodes_in_story( self.USER_ID, self.STORY_ID)): self.assertEqual(completed_node.to_dict(), story.story_contents.nodes[ind].to_dict())
def test_get_completed_nodes_in_story(self) -> None: story = story_fetchers.get_story_by_id(self.story_id) story_services.record_completed_node_in_story_context( # type: ignore[no-untyped-call] self.USER_ID, self.story_id, self.NODE_ID_1) story_services.record_completed_node_in_story_context( # type: ignore[no-untyped-call] self.USER_ID, self.story_id, self.NODE_ID_2) for ind, completed_node in enumerate( story_fetchers.get_completed_nodes_in_story( self.USER_ID, self.story_id)): self.assertEqual(completed_node.to_dict(), story.story_contents.nodes[ind].to_dict())
def get(self, story_id, node_id): """Handles GET requests.""" (_, _, classroom_url_fragment, topic_url_fragment, story_url_fragment, node_id) = self.request.path.split('/') story = story_fetchers.get_story_by_id(story_id) completed_nodes = story_fetchers.get_completed_nodes_in_story( self.user_id, story_id) ordered_nodes = story.story_contents.get_ordered_nodes() # In case the user is a returning user and has completed nodes in the # past, redirect to the story page so that the user can continue from # where they had left off. # If the node id is not the first node in the story, redirect to # the story page. if completed_nodes or node_id != ordered_nodes[0].id: self.redirect('/learn/%s/%s/story/%s' % (classroom_url_fragment, topic_url_fragment, story_url_fragment)) return (next_exp_ids, next_node_id, _) = (self._record_node_completion(story_id, node_id, [], ordered_nodes)) if next_node_id is None: self.redirect('/learn/%s/%s/story/%s' % (classroom_url_fragment, topic_url_fragment, story_url_fragment)) return redirect_url = '%s/%s' % (feconf.EXPLORATION_URL_PREFIX, next_exp_ids[0]) redirect_url = utils.set_url_query_parameter(redirect_url, 'classroom_url_fragment', classroom_url_fragment) redirect_url = utils.set_url_query_parameter(redirect_url, 'topic_url_fragment', topic_url_fragment) redirect_url = utils.set_url_query_parameter(redirect_url, 'story_url_fragment', story_url_fragment) redirect_url = utils.set_url_query_parameter(redirect_url, 'node_id', next_node_id) self.redirect(redirect_url)
def post(self, story_id, node_id): 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 = story.story_contents.get_ordered_nodes() (next_exp_ids, next_node_id, completed_node_ids) = (self._record_node_completion( story_id, node_id, completed_node_ids, ordered_nodes)) 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_fetchers.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_node_ids) == len(ordered_nodes) learner_at_review_point_in_story = ( len(exp_summaries) != 0 and (len(completed_node_ids) & 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 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) completed_node_ids = [ completed_node.id for completed_node in story_fetchers.get_completed_nodes_in_story( self.user_id, story_id) ] ordered_node_dicts = [ node.to_dict() for node in story.story_contents.get_ordered_nodes() # TODO(aks681): Once the story publication is done, add a check so # that only if all explorations in the story are published, can the # story itself be published. After which, remove the following # condition. if node.exploration_id ] for node in ordered_node_dicts: node['completed'] = False if node['id'] in completed_node_ids: node['completed'] = True exp_ids = [node['exploration_id'] for node in ordered_node_dicts] exp_summary_dicts = ( summary_services.get_displayable_exp_summary_dicts_matching_ids( exp_ids, user=self.user)) for ind, node in enumerate(ordered_node_dicts): node['exp_summary_dict'] = exp_summary_dicts[ind] self.values.update({ 'story_title': story.title, 'story_description': story.description, 'story_nodes': ordered_node_dicts }) self.render_json(self.values)
def get(self): """Handles GET requests.""" learner_progress_in_topics_and_stories = ( learner_progress_services.get_topics_and_stories_progress( self.user_id)[0]) all_topic_summary_dicts = ( learner_progress_services.get_displayable_topic_summary_dicts( self.user_id, learner_progress_in_topics_and_stories.all_topic_summaries)) completed_chapters_count = 0 for topic in all_topic_summary_dicts: for story in topic['canonical_story_summary_dict']: completed_chapters_count += (len( story_fetchers.get_completed_nodes_in_story( self.user_id, story['id']))) self.render_json({ 'completed_chapters_count': completed_chapters_count, })
def get(self, story_id): """Handles GET requests.""" story = story_fetchers.get_story_by_id(story_id) topic_id = story.corresponding_topic_id topic_name = topic_fetchers.get_topic_by_id(topic_id).name completed_node_ids = [ completed_node.id for completed_node in story_fetchers.get_completed_nodes_in_story( self.user_id, story_id) ] ordered_node_dicts = [ node.to_dict() for node in story.story_contents.get_ordered_nodes() ] for node in ordered_node_dicts: node['completed'] = False if node['id'] in completed_node_ids: node['completed'] = True exp_ids = [node['exploration_id'] for node in ordered_node_dicts] exp_summary_dicts = ( summary_services.get_displayable_exp_summary_dicts_matching_ids( exp_ids, user=self.user)) for ind, node in enumerate(ordered_node_dicts): node['exp_summary_dict'] = exp_summary_dicts[ind] self.values.update({ 'story_id': story.id, 'story_title': story.title, 'story_description': story.description, 'story_nodes': ordered_node_dicts, 'topic_name': topic_name, 'meta_tag_content': story.meta_tag_content }) self.render_json(self.values)
def get(self, topic_name): """Handles GET requests.""" topic = topic_fetchers.get_topic_by_name(topic_name) canonical_story_ids = topic.get_canonical_story_ids( include_only_published=True) additional_story_ids = topic.get_additional_story_ids( include_only_published=True) canonical_story_summaries = [ story_fetchers.get_story_summary_by_id( canonical_story_id) for canonical_story_id in canonical_story_ids] additional_story_summaries = [ story_fetchers.get_story_summary_by_id( additional_story_id) for additional_story_id in additional_story_ids] canonical_story_dicts = [] for story_summary in canonical_story_summaries: completed_node_titles = [ completed_node.title for completed_node in story_fetchers.get_completed_nodes_in_story( self.user_id, story_summary.id)] story_summary_dict = story_summary.to_human_readable_dict() story_summary_dict['story_is_published'] = True story_summary_dict['completed_node_titles'] = completed_node_titles canonical_story_dicts.append(story_summary_dict) additional_story_dicts = [] for story_summary in additional_story_summaries: completed_node_titles = [ completed_node.title for completed_node in story_fetchers.get_completed_nodes_in_story( self.user_id, story_summary.id)] story_summary_dict = story_summary.to_human_readable_dict() story_summary_dict['story_is_published'] = True story_summary_dict['completed_node_titles'] = completed_node_titles additional_story_dicts.append(story_summary_dict) uncategorized_skill_ids = topic.get_all_uncategorized_skill_ids() subtopics = topic.get_all_subtopics() all_skill_ids = topic.get_all_skill_ids() skill_descriptions, deleted_skill_ids = ( skill_services.get_descriptions_of_skills( all_skill_ids)) if deleted_skill_ids: deleted_skills_string = ', '.join(deleted_skill_ids) logging.error( 'The deleted skills: %s are still present in topic with id %s' % (deleted_skills_string, topic.id) ) if feconf.CAN_SEND_EMAILS: email_manager.send_mail_to_admin( 'Deleted skills present in topic', 'The deleted skills: %s are still present in topic with ' 'id %s' % (deleted_skills_string, topic.id)) if self.user_id: degrees_of_mastery = skill_services.get_multi_user_skill_mastery( self.user_id, all_skill_ids) else: degrees_of_mastery = {} for skill_id in all_skill_ids: degrees_of_mastery[skill_id] = None self.values.update({ 'topic_id': topic.id, 'topic_name': topic.name, 'topic_description': topic.description, 'canonical_story_dicts': canonical_story_dicts, 'additional_story_dicts': additional_story_dicts, 'uncategorized_skill_ids': uncategorized_skill_ids, 'subtopics': subtopics, 'degrees_of_mastery': degrees_of_mastery, 'skill_descriptions': skill_descriptions, 'practice_tab_is_displayed': topic.practice_tab_is_displayed }) self.render_json(self.values)
def post(self, story_id, node_id): story = story_fetchers.get_story_by_id(story_id) if story is None: logging.error('Could not find a story corresponding to ' '%s id.' % story_id) self.render_json({}) return topic = topic_fetchers.get_topic_by_id(story.corresponding_topic_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 = story.story_contents.get_ordered_nodes() (next_exp_ids, next_node_id, completed_node_ids) = (self._record_node_completion( story_id, node_id, completed_node_ids, ordered_nodes)) 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_fetchers.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_node_ids) == len(ordered_nodes) learner_at_review_point_in_story = ( len(exp_summaries) != 0 and (len(completed_node_ids) & 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 # If there is no next_node_id, the story is marked as completed else # mark the story as incomplete. if next_node_id is None: learner_progress_services.mark_story_as_completed( self.user_id, story_id) else: learner_progress_services.record_story_started( self.user_id, story.id) completed_story_ids = ( learner_progress_services.get_all_completed_story_ids( self.user_id)) story_ids_in_topic = [] for story in topic.canonical_story_references: story_ids_in_topic.append(story.story_id) is_topic_completed = set(story_ids_in_topic).intersection( set(completed_story_ids)) # If at least one story in the topic is completed, # mark the topic as learnt else mark it as partially learnt. if not is_topic_completed: learner_progress_services.record_topic_started( self.user_id, topic.id) else: learner_progress_services.mark_topic_as_learnt( self.user_id, topic.id) return self.render_json({ 'summaries': exp_summaries, 'ready_for_review_test': ready_for_review_test, 'next_node_id': next_node_id })
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_fetchers.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 })