Пример #1
0
    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
        })
Пример #2
0
    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)
Пример #3
0
    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)
Пример #4
0
    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())
Пример #5
0
 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())
Пример #6
0
 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())
Пример #7
0
    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)
Пример #8
0
    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
        })
Пример #9
0
    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)
Пример #10
0
    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,
        })
Пример #11
0
    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)
Пример #12
0
    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)
Пример #13
0
    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
        })
Пример #14
0
    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
        })