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)
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
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
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())
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)
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)
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)
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))
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)
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 }