def test_get_story_summary_by_id(self): story_summary = story_fetchers.get_story_summary_by_id(self.STORY_ID) self.assertEqual(story_summary.id, self.STORY_ID) self.assertEqual(story_summary.title, 'Title') self.assertEqual(story_summary.description, 'Description') self.assertEqual(story_summary.node_titles, ['Title 1']) self.assertEqual(story_summary.thumbnail_bg_color, None) self.assertEqual(story_summary.thumbnail_filename, None)
def test_to_human_readable_dict(self): story_summary = story_fetchers.get_story_summary_by_id(self.STORY_ID) expected_dict = { 'id': self.STORY_ID, 'title': 'Title', 'description': 'Description' } self.assertEqual(expected_dict, story_summary.to_human_readable_dict())
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 = [ summary.to_human_readable_dict() for summary in canonical_story_summaries ] additional_story_dicts = [ summary.to_human_readable_dict() for summary in additional_story_summaries ] uncategorized_skill_ids = topic.get_all_uncategorized_skill_ids() subtopics = topic.get_all_subtopics() self.values.update({ 'topic_id': topic.id, 'topic_name': topic.name, 'canonical_story_dicts': canonical_story_dicts, 'additional_story_dicts': additional_story_dicts, 'uncategorized_skill_ids': uncategorized_skill_ids, 'subtopics': subtopics }) self.render_json(self.values)
def get_canonical_story_dicts( user_id: str, topic: topic_domain.Topic ) -> List[CannonicalStoryDict]: """Returns a list of canonical story dicts in the topic. Args: user_id: str. The ID of the user. topic: Topic. The topic domain object. Returns: list(dict). A list of canonical story dicts in the given topic. """ canonical_story_ids: List[str] = topic.get_canonical_story_ids( include_only_published=True) canonical_story_summaries: List[Optional[story_domain.StorySummary]] = [ story_fetchers.get_story_summary_by_id( canonical_story_id) for canonical_story_id in canonical_story_ids] canonical_story_dicts = [] for story_summary in canonical_story_summaries: # Ruling out the possibility of None for mypy type checking. assert story_summary is not None pending_and_all_nodes_in_story = ( story_fetchers.get_pending_and_all_nodes_in_story( user_id, story_summary.id)) all_nodes = pending_and_all_nodes_in_story['all_nodes'] pending_nodes = pending_and_all_nodes_in_story['pending_nodes'] pending_node_titles = [node.title for node in pending_nodes] completed_node_titles = utils.compute_list_difference( story_summary.node_titles, pending_node_titles) # Here, the return type of 'to_human_readable_dict()' method is # HumanReadableStorySummaryDict which does not have topic_url_fragment, # story_is_published and other keys. To overcome this missing keys # issues, we defined a CannonicalStoryDict and assigned it to the # `story_summary_dict`. Due this a conflict in type assignment is # raised which cause MyPy to throw `Incompatible types in assignment` # error. Thus to avoid error, we used ignore here. story_summary_dict: CannonicalStoryDict = ( story_summary.to_human_readable_dict() # type: ignore[assignment] ) story_summary_dict['topic_url_fragment'] = topic.url_fragment story_summary_dict['classroom_url_fragment'] = ( classroom_services.get_classroom_url_fragment_for_topic_id( topic.id)) story_summary_dict['story_is_published'] = True story_summary_dict['completed_node_titles'] = completed_node_titles story_summary_dict['all_node_dicts'] = [ node.to_dict() for node in all_nodes] canonical_story_dicts.append(story_summary_dict) return canonical_story_dicts
def compute_summary_of_topic(topic): """Create a TopicSummary domain object for a given Topic domain object and return it. Args: topic: Topic. The topic object for which the summary is to be computed. Returns: TopicSummary. The computed summary for the given topic. """ canonical_story_count = 0 additional_story_count = 0 published_node_count = 0 for reference in topic.canonical_story_references: if reference.story_is_published: canonical_story_count += 1 story_summary = story_fetchers.get_story_summary_by_id( reference.story_id) published_node_count += len(story_summary.node_titles) for reference in topic.additional_story_references: if reference.story_is_published: additional_story_count += 1 topic_model_canonical_story_count = canonical_story_count topic_model_additional_story_count = additional_story_count total_model_published_node_count = published_node_count topic_model_uncategorized_skill_count = len(topic.uncategorized_skill_ids) topic_model_subtopic_count = len(topic.subtopics) total_skill_count = topic_model_uncategorized_skill_count for subtopic in topic.subtopics: total_skill_count = total_skill_count + len(subtopic.skill_ids) topic_summary = topic_domain.TopicSummary( topic.id, topic.name, topic.canonical_name, topic.language_code, topic.description, topic.version, topic_model_canonical_story_count, topic_model_additional_story_count, topic_model_uncategorized_skill_count, topic_model_subtopic_count, total_skill_count, total_model_published_node_count, topic.thumbnail_filename, topic.thumbnail_bg_color, topic.url_fragment, topic.created_on, topic.last_updated ) return topic_summary
def get_canonical_story_dicts(user_id, topic): """Returns a list of canonical story dicts in the topic. Args: user_id: str. The ID of the user. topic: Topic. The topic domain object. Returns: list(dict). A list of canonical story dicts in the given topic. """ canonical_story_ids = topic.get_canonical_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 ] canonical_story_dicts = [] for story_summary in canonical_story_summaries: pending_and_all_nodes_in_story = ( story_fetchers.get_pending_and_all_nodes_in_story( user_id, story_summary.id)) all_nodes = pending_and_all_nodes_in_story['all_nodes'] pending_nodes = pending_and_all_nodes_in_story['pending_nodes'] pending_node_titles = [node.title for node in pending_nodes] completed_node_titles = utils.compute_list_difference( story_summary.node_titles, pending_node_titles) story_summary_dict = story_summary.to_human_readable_dict() story_summary_dict['topic_url_fragment'] = topic.url_fragment story_summary_dict['classroom_url_fragment'] = ( classroom_services.get_classroom_url_fragment_for_topic_id( topic.id)) story_summary_dict['story_is_published'] = True story_summary_dict['completed_node_titles'] = completed_node_titles story_summary_dict['all_node_dicts'] = [ node.to_dict() for node in all_nodes ] canonical_story_dicts.append(story_summary_dict) return canonical_story_dicts
def test_update_story_node_properties(self): changelist = [ story_domain.StoryChange({ 'cmd': story_domain.CMD_ADD_STORY_NODE, 'node_id': self.NODE_ID_2, 'title': 'Title 2' }), story_domain.StoryChange({ 'cmd': story_domain.CMD_UPDATE_STORY_NODE_PROPERTY, 'property_name': (story_domain.STORY_NODE_PROPERTY_DESTINATION_NODE_IDS), 'node_id': self.NODE_ID_2, 'old_value': [], 'new_value': [self.NODE_ID_1] }), story_domain.StoryChange({ 'cmd': story_domain.CMD_UPDATE_STORY_NODE_OUTLINE_STATUS, 'node_id': self.NODE_ID_2, 'old_value': False, 'new_value': True }), story_domain.StoryChange({ 'cmd': story_domain.CMD_UPDATE_STORY_CONTENTS_PROPERTY, 'property_name': (story_domain.INITIAL_NODE_ID), 'old_value': self.NODE_ID_1, 'new_value': self.NODE_ID_2 }) ] story_services.update_story(self.USER_ID, self.STORY_ID, changelist, 'Added story node.') story = story_fetchers.get_story_by_id(self.STORY_ID) self.assertEqual(story.story_contents.nodes[1].destination_node_ids, [self.NODE_ID_1]) self.assertEqual(story.story_contents.nodes[1].outline_is_finalized, True) self.assertEqual(story.story_contents.nodes[1].title, 'Title 2') self.assertEqual(story.story_contents.initial_node_id, self.NODE_ID_2) self.assertEqual(story.story_contents.next_node_id, 'node_3') self.assertEqual(story.version, 3) story_summary = story_fetchers.get_story_summary_by_id(self.STORY_ID) self.assertEqual(story_summary.node_count, 2) changelist = [ story_domain.StoryChange({ 'cmd': story_domain.CMD_DELETE_STORY_NODE, 'node_id': self.NODE_ID_1 }), story_domain.StoryChange({ 'cmd': story_domain.CMD_UPDATE_STORY_NODE_OUTLINE_STATUS, 'node_id': self.NODE_ID_2, 'old_value': True, 'new_value': False }), story_domain.StoryChange({ 'cmd': story_domain.CMD_UPDATE_STORY_NODE_PROPERTY, 'property_name': (story_domain.STORY_NODE_PROPERTY_TITLE), 'node_id': self.NODE_ID_2, 'old_value': 'Title 2', 'new_value': 'Modified title 2' }), ] story_services.update_story(self.USER_ID, self.STORY_ID, changelist, 'Removed a story node.') story_summary = story_fetchers.get_story_summary_by_id(self.STORY_ID) story = story_fetchers.get_story_by_id(self.STORY_ID) self.assertEqual(story_summary.node_count, 1) self.assertEqual(story.story_contents.nodes[0].title, 'Modified title 2') self.assertEqual(story.story_contents.nodes[0].destination_node_ids, []) self.assertEqual(story.story_contents.nodes[0].outline_is_finalized, False)
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 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 = [ summary.to_human_readable_dict() for summary in canonical_story_summaries ] additional_story_dicts = [ summary.to_human_readable_dict() for summary in additional_story_summaries ] uncategorized_skill_ids = topic.get_all_uncategorized_skill_ids() subtopics = topic.get_all_subtopics() assigned_skill_ids = topic.get_all_skill_ids() skill_descriptions, deleted_skill_ids = ( skill_services.get_descriptions_of_skills(assigned_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, assigned_skill_ids) else: degrees_of_mastery = {} for skill_id in assigned_skill_ids: degrees_of_mastery[skill_id] = None self.values.update({ 'topic_id': topic.id, 'topic_name': topic.name, '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 }) self.render_json(self.values)