Ejemplo n.º 1
0
    def get(self, skill_id):
        """Populates the data on the individual skill page."""

        skill = skill_fetchers.get_skill_by_id(skill_id, strict=False)

        if skill is None:
            raise self.PageNotFoundException(
                Exception('The skill with the given id doesn\'t exist.'))

        topics = topic_fetchers.get_all_topics()
        grouped_skill_summary_dicts = {}
        # It might be the case that the requested skill is not assigned to any
        # topic, or it might be assigned to a topic and not a subtopic, or
        # it can be assigned to multiple topics, so this dict represents
        # a key value pair where key is topic's name and value is subtopic
        # name which might be None indicating that the skill is assigned
        # to that topic but not to a subtopic.
        assigned_skill_topic_data_dict = {}

        for topic in topics:
            skill_ids_in_topic = topic.get_all_skill_ids()
            if skill_id in skill_ids_in_topic:
                subtopic_name = None
                for subtopic in topic.subtopics:
                    if skill_id in subtopic.skill_ids:
                        subtopic_name = subtopic.title
                        break
                assigned_skill_topic_data_dict[topic.name] = subtopic_name

            skill_summaries = skill_services.get_multi_skill_summaries(
                skill_ids_in_topic)
            skill_summary_dicts = [
                summary.to_dict() for summary in skill_summaries
            ]
            grouped_skill_summary_dicts[topic.name] = skill_summary_dicts

        self.values.update({
            'skill':
            skill.to_dict(),
            'assigned_skill_topic_data_dict':
            assigned_skill_topic_data_dict,
            'grouped_skill_summaries':
            grouped_skill_summary_dicts
        })

        self.render_json(self.values)
Ejemplo n.º 2
0
def get_categorized_skill_ids_and_descriptions():
    """Returns a CategorizedSkills domain object for all the skills that are
    categorized.

    Returns:
        CategorizedSkills. An instance of the CategorizedSkills domain object
        for all the skills that are categorized.
    """
    topics = topic_fetchers.get_all_topics()

    categorized_skills = skill_domain.CategorizedSkills()

    skill_ids = []

    for topic in topics:
        subtopics = topic.subtopics
        subtopic_titles = [subtopic.title for subtopic in subtopics]
        categorized_skills.add_topic(topic.name, subtopic_titles)
        for skill_id in topic.uncategorized_skill_ids:
            skill_ids.append(skill_id)
        for subtopic in subtopics:
            for skill_id in subtopic.skill_ids:
                skill_ids.append(skill_id)

    skill_descriptions = get_descriptions_of_skills(skill_ids)[0]

    for topic in topics:
        subtopics = topic.subtopics
        for skill_id in topic.uncategorized_skill_ids:
            categorized_skills.add_uncategorized_skill(
                topic.name, skill_id,
                skill_descriptions[skill_id])
        for subtopic in subtopics:
            for skill_id in subtopic.skill_ids:
                categorized_skills.add_subtopic_skill(
                    topic.name, subtopic.title,
                    skill_id, skill_descriptions[skill_id])

    return categorized_skills
Ejemplo n.º 3
0
def get_all_topic_assignments_for_skill(skill_id):
    """Returns a list containing all the topics to which the given skill is
    assigned along with topic details.

    Args:
        skill_id: str. ID of the skill.

    Returns:
        list(TopicAssignment). A list of TopicAssignment domain objects.
    """
    topic_assignments = []
    topics = topic_fetchers.get_all_topics()
    for topic in topics:
        if skill_id in topic.get_all_skill_ids():
            subtopic_id = None
            for subtopic in topic.subtopics:
                if skill_id in subtopic.skill_ids:
                    subtopic_id = subtopic.id
                    break

            topic_assignments.append(skill_domain.TopicAssignment(
                topic.id, topic.name, topic.version, subtopic_id))

    return topic_assignments
Ejemplo n.º 4
0
    def _get_reviewable_exploration_opportunity_summaries(
        self, user_id, topic_name
    ):
        """Returns exploration opportunity summaries that have translation
        suggestions that are reviewable by the supplied user. The result is
        sorted in descending order by topic, story, and story node order.

        Args:
            user_id: str. The user ID of the user for which to filter
                translation suggestions.
            topic_name: str. A topic name for which to filter the exploration
                opportunity summaries. If 'All' is supplied, all available
                exploration opportunity summaries will be returned.

        Returns:
            list(ExplorationOpportunitySummary). A list of the matching
            exploration opportunity summaries.
        """
        # 1. Fetch the eligible topics.
        # 2. Fetch the stories for the topics.
        # 3. Get the reviewable translation suggestion target IDs for the user.
        # 4. Get story exploration nodes in order, filtering for explorations
        # that have in review translation suggestions.
        if topic_name is None:
            topics = topic_fetchers.get_all_topics()
        else:
            topic = topic_fetchers.get_topic_by_name(topic_name)
            if topic is None:
                raise self.InvalidInputException(
                    'The supplied input topic: %s is not valid' % topic_name)
            topics = [topic]
        topic_stories = story_fetchers.get_stories_by_ids([
            reference.story_id
            for topic in topics
            for reference in topic.get_all_story_references()
            if reference.story_is_published])
        topic_exp_ids = [
            node.exploration_id
            for story in topic_stories
            for node in story.story_contents.get_ordered_nodes()
        ]
        in_review_suggestions, _ = (
            suggestion_services
            .get_reviewable_translation_suggestions_by_offset(
                user_id, topic_exp_ids, None, 0))
        # Filter out suggestions that should not be shown to the user.
        # This is defined as a set as we only care about the unique IDs.
        in_review_suggestion_target_ids = {
            suggestion.target_id
            for suggestion in
            suggestion_services.get_suggestions_with_translatable_explorations(
                in_review_suggestions)
        }
        exp_ids = [
            exp_id
            for exp_id in topic_exp_ids
            if exp_id in in_review_suggestion_target_ids
        ]
        return (
            opportunity_services.get_exploration_opportunity_summaries_by_ids(
                exp_ids).values())
Ejemplo n.º 5
0
    def get(self, topic_id):
        """Populates the data on the individual topic page."""
        topic = topic_fetchers.get_topic_by_id(topic_id, strict=False)

        if topic is None:
            raise self.PageNotFoundException(
                Exception('The topic with the given id doesn\'t exist.'))

        skill_id_to_description_dict, deleted_skill_ids = (
            skill_services.get_descriptions_of_skills(
                topic.get_all_skill_ids()))

        topics = topic_fetchers.get_all_topics()
        grouped_skill_summary_dicts = {}
        skill_id_to_rubrics_dict = {}

        for topic_object in topics:
            skill_id_to_rubrics_dict_local, deleted_skill_ids = (
                skill_services.get_rubrics_of_skills(
                    topic_object.get_all_skill_ids())
            )

            skill_id_to_rubrics_dict.update(skill_id_to_rubrics_dict_local)

            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))
            skill_summaries = skill_services.get_multi_skill_summaries(
                topic_object.get_all_skill_ids())
            skill_summary_dicts = [
                summary.to_dict() for summary in skill_summaries]
            grouped_skill_summary_dicts[topic_object.name] = skill_summary_dicts

        classroom_url_fragment = (
            classroom_services.get_classroom_url_fragment_for_topic_id(
                topic_id))
        skill_question_count_dict = {}
        for skill_id in topic.get_all_skill_ids():
            skill_question_count_dict[skill_id] = (
                question_services.get_total_question_count_for_skill_ids(
                    [skill_id]))
        skill_creation_is_allowed = (
            role_services.ACTION_CREATE_NEW_SKILL in self.user.actions)

        self.values.update({
            'classroom_url_fragment': classroom_url_fragment,
            'topic_dict': topic.to_dict(),
            'grouped_skill_summary_dicts': grouped_skill_summary_dicts,
            'skill_question_count_dict': skill_question_count_dict,
            'skill_id_to_description_dict': skill_id_to_description_dict,
            'skill_id_to_rubrics_dict': skill_id_to_rubrics_dict,
            'skill_creation_is_allowed': skill_creation_is_allowed
        })

        self.render_json(self.values)
Ejemplo n.º 6
0
 def test_get_all_topics(self):
     topics = topic_fetchers.get_all_topics()
     self.assertEqual(len(topics), 1)
     self.assertEqual(topics[0].id, self.topic.id)
Ejemplo n.º 7
0
    def get(self):
        """Handles GET requests."""

        topic_summaries = topic_services.get_all_topic_summaries()
        topic_summary_dicts = [
            summary.to_dict() for summary in topic_summaries]

        skill_summaries = skill_services.get_all_skill_summaries()
        skill_summary_dicts = [
            summary.to_dict() for summary in skill_summaries]

        skill_ids_assigned_to_some_topic = (
            topic_services.get_all_skill_ids_assigned_to_some_topic())
        merged_skill_ids = (
            skill_services.get_merged_skill_ids())
        topic_rights_dict = topic_services.get_all_topic_rights()
        for topic_summary in topic_summary_dicts:
            if topic_rights_dict[topic_summary['id']]:
                topic_rights = topic_rights_dict[topic_summary['id']]
                if topic_rights:
                    topic_summary['is_published'] = (
                        topic_rights.topic_is_published)
                    topic_summary['can_edit_topic'] = (
                        topic_services.check_can_edit_topic(
                            self.user, topic_rights)
                    )

        all_classrooms_dict = config_domain.TOPIC_IDS_FOR_CLASSROOM_PAGES.value
        all_classroom_names = [
            classroom['name'] for classroom in all_classrooms_dict]

        for topic_summary_dict in topic_summary_dicts:
            topic_summary_dict['classroom'] = None
            for classroom in all_classrooms_dict:
                if topic_summary_dict['id'] in classroom['topic_ids']:
                    topic_summary_dict['classroom'] = classroom['name']
                    break

        untriaged_skill_summary_dicts = []
        mergeable_skill_summary_dicts = []
        categorized_skills_dict = {}
        topics = topic_fetchers.get_all_topics()
        for topic in topics:
            subtopics = topic.subtopics
            categorized_skills_dict[topic.name] = {}
            uncategorized_skills = (
                skill_services.get_descriptions_of_skills(
                    topic.uncategorized_skill_ids)[0])
            skills_list = []
            for skill_id in topic.uncategorized_skill_ids:
                skill_dict = {
                    'skill_id': skill_id,
                    'skill_description': uncategorized_skills[skill_id]
                }
                skills_list.append(skill_dict)
            categorized_skills_dict[topic.name]['uncategorized'] = (
                skills_list)
            for subtopic in subtopics:
                skills = (skill_services.get_descriptions_of_skills(
                    subtopic.skill_ids))[0]
                skills_list = []
                for skill_id in subtopic.skill_ids:
                    skill_dict = {
                        'skill_id': skill_id,
                        'skill_description': skills[skill_id]
                    }
                    skills_list.append(skill_dict)
                categorized_skills_dict[topic.name][
                    subtopic.title] = skills_list
        categorized_skills_dict['untriaged_skills'] = []
        for skill_summary_dict in skill_summary_dicts:
            skill_id = skill_summary_dict['id']
            if (skill_id not in skill_ids_assigned_to_some_topic) and (
                    skill_id not in merged_skill_ids):
                untriaged_skill_summary_dicts.append(skill_summary_dict)
                categorized_skills_dict['untriaged_skills'].append({
                    'skill_id': skill_id,
                    'skill_description': skill_summary_dict['description']
                })
            if (skill_id in skill_ids_assigned_to_some_topic) and (
                    skill_id not in merged_skill_ids):
                mergeable_skill_summary_dicts.append(skill_summary_dict)

        can_delete_topic = (
            role_services.ACTION_DELETE_TOPIC in self.user.actions)

        can_create_topic = (
            role_services.ACTION_CREATE_NEW_TOPIC in self.user.actions)

        can_delete_skill = (
            role_services.ACTION_DELETE_ANY_SKILL in self.user.actions)

        can_create_skill = (
            role_services.ACTION_CREATE_NEW_SKILL in self.user.actions)

        self.values.update({
            'untriaged_skill_summary_dicts': untriaged_skill_summary_dicts,
            'mergeable_skill_summary_dicts': mergeable_skill_summary_dicts,
            'topic_summary_dicts': topic_summary_dicts,
            'all_classroom_names': all_classroom_names,
            'can_delete_topic': can_delete_topic,
            'can_create_topic': can_create_topic,
            'can_delete_skill': can_delete_skill,
            'can_create_skill': can_create_skill,
            'categorized_skills_dict': categorized_skills_dict
        })
        self.render_json(self.values)
Ejemplo n.º 8
0
def replace_skill_id_in_all_topics(user_id, old_skill_id, new_skill_id):
    """Replaces the old skill id with the new one in all the associated topics.

    Args:
        user_id: str. The unique user ID of the user.
        old_skill_id: str. The old skill id.
        new_skill_id: str. The new skill id.
    """
    all_topics = topic_fetchers.get_all_topics()
    for topic in all_topics:
        change_list = []
        if old_skill_id in topic.get_all_skill_ids():
            if new_skill_id in topic.get_all_skill_ids():
                raise Exception(
                    'Found topic \'%s\' contains the two skills to be merged. '
                    'Please unassign one of these skills from topic '
                    'and retry this operation.' % topic.name)
            if old_skill_id in topic.uncategorized_skill_ids:
                change_list.extend([
                    topic_domain.TopicChange({
                        'cmd':
                        'remove_uncategorized_skill_id',
                        'uncategorized_skill_id':
                        old_skill_id
                    }),
                    topic_domain.TopicChange({
                        'cmd':
                        'add_uncategorized_skill_id',
                        'new_uncategorized_skill_id':
                        new_skill_id
                    })
                ])
            for subtopic in topic.subtopics:
                if old_skill_id in subtopic.skill_ids:
                    change_list.extend([
                        topic_domain.TopicChange({
                            'cmd':
                            topic_domain.CMD_REMOVE_SKILL_ID_FROM_SUBTOPIC,
                            'subtopic_id': subtopic.id,
                            'skill_id': old_skill_id
                        }),
                        topic_domain.TopicChange({
                            'cmd':
                            'remove_uncategorized_skill_id',
                            'uncategorized_skill_id':
                            old_skill_id
                        }),
                        topic_domain.TopicChange({
                            'cmd':
                            'add_uncategorized_skill_id',
                            'new_uncategorized_skill_id':
                            new_skill_id
                        }),
                        topic_domain.TopicChange({
                            'cmd': topic_domain.CMD_MOVE_SKILL_ID_TO_SUBTOPIC,
                            'old_subtopic_id': None,
                            'new_subtopic_id': subtopic.id,
                            'skill_id': new_skill_id
                        })
                    ])
                    break
            topic_services.update_topic_and_subtopic_pages(
                user_id, topic.id, change_list,
                'Replace skill id %s with skill id %s in the topic' %
                (old_skill_id, new_skill_id))
Ejemplo n.º 9
0
 def test_get_all_topics(self) -> None:
     topics = topic_fetchers.get_all_topics()
     self.assertEqual(len(topics), 1)
     # Ruling out the possibility of None for mypy type checking.
     assert self.topic is not None
     self.assertEqual(topics[0].id, self.topic.id)
Ejemplo n.º 10
0
def get_matching_learner_group_syllabus_to_add(
        learner_group_id: str, keyword: str, search_type: str, category: str,
        language_code: str) -> learner_group_domain.LearnerGroupSyllabusDict:
    """Returns the syllabus of items matching the given filter arguments
    that can be added to the learner group.

    Args:
        learner_group_id: str. The id of the learner group.
        keyword: str. The keyword to search the syllabus. It is compared with
            the title of the topics, stories and subtopics.
        search_type: str. The type of the syllabus item to search. It can be
            either 'Story' or 'Skill'.
        category: str. The category of the syllabus items. It is the
            classroom in which the stories and subtopics are to be searched.
        language_code: str. The language of the topics in which the stories
            and subtopics are to be searched.

    Returns:
        dict. The matching syllabus items to add to the learner group.
    """
    # Default case when syllabus is being added to a new group.
    group_subtopic_page_ids: List[str] = []
    group_story_ids: List[str] = []

    # Case when syllabus is being added to an existing group.
    if learner_group_id:
        learner_group_model = learner_group_models.LearnerGroupModel.get(
            learner_group_id, strict=True)
        group_subtopic_page_ids = learner_group_model.subtopic_page_ids
        group_story_ids = learner_group_model.story_ids

    matching_topic_ids: List[str] = []
    all_classrooms_dict = config_domain.CLASSROOM_PAGES_DATA.value

    matching_subtopics_dicts: List[
        subtopic_page_domain.SubtopicPageSummaryDict] = []
    matching_story_syllabus_item_dicts: List[
        story_domain.LearnerGroupSyllabusStorySummaryDict] = []

    if category != constants.DEFAULT_ADD_SYLLABUS_FILTER:
        for classroom in all_classrooms_dict:
            if category and classroom['name'] == category:
                matching_topic_ids.extend(classroom['topic_ids'])
        matching_topics_with_none: Sequence[Optional[topic_domain.Topic]] = (
            topic_fetchers.get_topics_by_ids(matching_topic_ids))
    else:
        matching_topics_with_none = topic_fetchers.get_all_topics()

    keyword = keyword.lower()
    for topic in matching_topics_with_none:
        # Ruling out the possibility of None for mypy type checking.
        assert topic is not None
        if language_code not in (constants.DEFAULT_ADD_SYLLABUS_FILTER,
                                 topic.language_code):
            continue

        if keyword in topic.canonical_name:
            # If search type is set to default or search type is set to
            # 'Story', add all story ids of this topic to the filtered
            # story ids.
            if (search_type in (constants.LEARNER_GROUP_ADD_STORY_FILTER,
                                constants.DEFAULT_ADD_SYLLABUS_FILTER)):
                matching_story_syllabus_item_dicts.extend(
                    get_matching_story_syllabus_item_dicts(
                        topic, group_story_ids))

            # If search type is set to default or search type is set to
            # 'Skill', add all subtopics of this topic to the filtered
            # subtopics.
            if (search_type in (constants.LEARNER_GROUP_ADD_SKILL_FILTER,
                                constants.DEFAULT_ADD_SYLLABUS_FILTER)):
                matching_subtopics_dicts.extend(
                    get_matching_subtopic_syllabus_item_dicts(
                        topic, group_subtopic_page_ids))
        else:
            # If search type is set to default or search type is set to
            # 'Skill', add the subtopics which have the keyword in their
            # title to the filtered subtopics.
            if (search_type in (constants.LEARNER_GROUP_ADD_SKILL_FILTER,
                                constants.DEFAULT_ADD_SYLLABUS_FILTER)):
                matching_subtopics_dicts.extend(
                    get_matching_subtopic_syllabus_item_dicts(
                        topic, group_subtopic_page_ids, keyword))

            # If search type is set to default or search type is set to
            # 'Story', add all story ids of this topic to the possible
            # story ids.
            if (search_type in (constants.LEARNER_GROUP_ADD_STORY_FILTER,
                                constants.DEFAULT_ADD_SYLLABUS_FILTER)):
                matching_story_syllabus_item_dicts.extend(
                    get_matching_story_syllabus_item_dicts(
                        topic, group_story_ids, keyword))

    return {
        'story_summary_dicts': matching_story_syllabus_item_dicts,
        'subtopic_summary_dicts': matching_subtopics_dicts
    }