Пример #1
0
 def test_get_questions_by_skill_ids_raise_error(self):
     with self.assertRaisesRegexp(
             Exception,
             'Question count is too high, please limit the question '
             'count to %d.' % feconf.MAX_QUESTIONS_FETCHABLE_AT_ONE_TIME):
         question_services.get_questions_by_skill_ids(
             25, ['skill_1', 'skill_2'])
Пример #2
0
    def get(self):
        """Handles GET request."""
        # Skill ids are given as a comma separated list because this is
        # a GET request.

        skill_ids = self.request.get('skill_ids').split(',')
        question_count = self.request.get('question_count')
        fetch_by_difficulty_value = self.request.get('fetch_by_difficulty')

        if not question_count.isdigit() or int(question_count) <= 0:
            raise self.InvalidInputException(
                'Question count has to be greater than 0')

        if not (fetch_by_difficulty_value == 'true' or
                fetch_by_difficulty_value == 'false'):
            raise self.InvalidInputException(
                'fetch_by_difficulty must be true or false')
        fetch_by_difficulty = (fetch_by_difficulty_value == 'true')

        questions = (
            question_services.get_questions_by_skill_ids(
                int(question_count), skill_ids, fetch_by_difficulty)
        )

        question_dicts = [question.to_dict() for question in questions]
        self.values.update({
            'question_dicts': question_dicts
        })
        self.render_json(self.values)
Пример #3
0
    def get(self):
        """Handles GET request."""
        # Skill ids are given as a comma separated list because this is
        # a GET request.

        skill_ids = self.request.get('skill_ids').split(',')
        question_count = self.request.get('question_count')
        fetch_by_difficulty_value = self.request.get('fetch_by_difficulty')

        if not question_count.isdigit() or int(question_count) <= 0:
            raise self.InvalidInputException(
                'Question count has to be greater than 0')

        if fetch_by_difficulty_value not in ('true', 'false'):
            raise self.InvalidInputException(
                'fetch_by_difficulty must be true or false')
        fetch_by_difficulty = (fetch_by_difficulty_value == 'true')

        if len(skill_ids) > feconf.MAX_NUMBER_OF_SKILL_IDS:
            skill_ids = skill_services.filter_skills_by_mastery(
                self.user_id, skill_ids)

        questions = (question_services.get_questions_by_skill_ids(
            int(question_count), skill_ids, fetch_by_difficulty))
        random.shuffle(questions)

        question_dicts = [question.to_dict() for question in questions]
        self.values.update(
            {'question_dicts': question_dicts[:feconf.QUESTION_BATCH_SIZE]})
        self.render_json(self.values)
Пример #4
0
 def test_get_questions_by_skill_ids(self):
     question_services.create_new_question_skill_link(
         self.question_id, 'skill_1')
     questions, _ = (question_services.get_questions_by_skill_ids(
         2, ['skill_1'], ''))
     self.assertEqual(len(questions), 1)
     self.assertEqual(questions[0].to_dict(), self.question.to_dict())
Пример #5
0
    def get(self, exploration_id):
        """Handles GET request."""
        start_cursor = self.request.get('cursor')
        story_id = self.request.get('story_id')
        story = story_services.get_story_by_id(story_id, strict=False)
        if story is None:
            raise self.InvalidInputException

        if not story.has_exploration(exploration_id):
            raise self.InvalidInputException

        pretest_questions, next_start_cursor = (
            question_services.get_questions_by_skill_ids(
                feconf.NUM_PRETEST_QUESTIONS,
                story.get_prerequisite_skill_ids_for_exp_id(exploration_id),
                start_cursor))
        pretest_question_dicts = [
            question.to_dict() for question in pretest_questions
        ]

        self.values.update({
            'pretest_question_dicts': pretest_question_dicts,
            'next_start_cursor': next_start_cursor
        })
        self.render_json(self.values)
Пример #6
0
    def test_get_questions_by_skill_ids_with_fetch_by_difficulty(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_1, 'skill_2', 0.8)
        question_services.create_new_question_skill_link(
            self.editor_id, self.question_id_2, 'skill_2', 0.5)

        questions = question_services.get_questions_by_skill_ids(
            2, ['skill_1', 'skill_2'], True)
        questions.sort(key=lambda question: question.last_updated)

        self.assertEqual(len(questions), 2)
        self.assertEqual(questions[0].to_dict(), self.question.to_dict())
        self.assertEqual(questions[1].to_dict(), self.question_2.to_dict())
Пример #7
0
    def test_get_questions_by_skill_ids(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_1, 'skill_2', 0.8)
        question_services.create_new_question_skill_link(
            self.editor_id, self.question_id_2, 'skill_2', 0.5)

        questions = question_services.get_questions_by_skill_ids(
            4, ['skill_1', 'skill_2'])
        questions.sort(key=lambda question: question.last_updated)

        self.assertEqual(len(questions), 3)
        self.assertEqual(questions[0].to_dict(), self.question.to_dict())
        self.assertEqual(questions[1].to_dict(), self.question_1.to_dict())
        self.assertEqual(questions[2].to_dict(), self.question_2.to_dict())
Пример #8
0
    def get(self, exploration_id):
        """Handles GET request."""
        story_url_fragment = self.request.get('story_url_fragment')
        story = story_fetchers.get_story_by_url_fragment(story_url_fragment)
        if story is None:
            raise self.InvalidInputException
        if not story.has_exploration(exploration_id):
            raise self.InvalidInputException
        pretest_questions = (question_services.get_questions_by_skill_ids(
            feconf.NUM_PRETEST_QUESTIONS,
            story.get_prerequisite_skill_ids_for_exp_id(exploration_id), True))
        question_dicts = [question.to_dict() for question in pretest_questions]

        self.values.update({
            'pretest_question_dicts': question_dicts,
        })
        self.render_json(self.values)
Пример #9
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
        })
Пример #10
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
        })
Пример #11
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
        })
Пример #12
0
    def get(self, topic_name):
        """Handles GET requests."""

        if not constants.ENABLE_NEW_STRUCTURE_PLAYERS:
            raise self.PageNotFoundException

        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

        train_tab_should_be_displayed = False
        if all_skill_ids:
            questions = question_services.get_questions_by_skill_ids(
                5, all_skill_ids, False)
            if len(questions) == 5:
                train_tab_should_be_displayed = True

        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,
            'train_tab_should_be_displayed':
            train_tab_should_be_displayed
        })
        self.render_json(self.values)
Пример #13
0
    def get(self, exploration_id):
        """Populates the data on the individual exploration page.

        Args:
            exploration_id: str. The ID of the exploration.
        """
        version = self.request.get('v')
        story_id = self.request.get('story_id')
        version = int(version) if version else None

        try:
            exploration = exp_services.get_exploration_by_id(
                exploration_id, version=version)
        except Exception as e:
            raise self.PageNotFoundException(e)

        exploration_rights = rights_manager.get_exploration_rights(
            exploration_id, strict=False)
        user_settings = user_services.get_user_settings(self.user_id)

        preferred_audio_language_code = None
        if user_settings is not None:
            preferred_audio_language_code = (
                user_settings.preferred_audio_language_code)

        # Retrieve all classifiers for the exploration.
        state_classifier_mapping = {}
        classifier_training_jobs = (
            classifier_services.get_classifier_training_jobs(
                exploration_id, exploration.version, exploration.states))
        for index, state_name in enumerate(exploration.states):
            if classifier_training_jobs[index] is not None:
                classifier_data = classifier_training_jobs[
                    index].classifier_data
                algorithm_id = classifier_training_jobs[index].algorithm_id
                data_schema_version = (
                    classifier_training_jobs[index].data_schema_version)
                state_classifier_mapping[state_name] = {
                    'algorithm_id': algorithm_id,
                    'classifier_data': classifier_data,
                    'data_schema_version': data_schema_version
                }

        pretest_question_dicts = []
        next_cursor = None
        if story_id:
            story = story_services.get_story_by_id(story_id, strict=False)
            if story is None:
                raise self.InvalidInputException

            if not story.has_exploration(exploration_id):
                raise self.InvalidInputException

            pretest_questions, next_cursor = (
                question_services.get_questions_by_skill_ids(
                    feconf.NUM_PRETEST_QUESTIONS,
                    story.get_prerequisite_skill_ids_for_exp_id(exploration_id),
                    '')
            )
            pretest_question_dicts = [
                question.to_dict() for question in pretest_questions
            ]

        self.values.update({
            'can_edit': (
                rights_manager.check_can_edit_activity(
                    self.user, exploration_rights)),
            'exploration': exploration.to_player_dict(),
            'exploration_id': exploration_id,
            'pretest_question_dicts': pretest_question_dicts,
            'next_cursor_for_pretests': next_cursor,
            'is_logged_in': bool(self.user_id),
            'session_id': utils.generate_new_session_id(),
            'version': exploration.version,
            'preferred_audio_language_code': preferred_audio_language_code,
            'state_classifier_mapping': state_classifier_mapping,
            'auto_tts_enabled': exploration.auto_tts_enabled,
            'correctness_feedback_enabled': (
                exploration.correctness_feedback_enabled),
        })
        self.render_json(self.values)