def get(self, topic_name): # Topic cannot be None as an exception will be thrown from its decorator # if so. topic = topic_fetchers.get_topic_by_name(topic_name) selected_subtopic_ids = ( self.normalized_request.get('selected_subtopic_ids')) selected_skill_ids = [] for subtopic in topic.subtopics: # An error is not thrown here, since it's fine to just ignore the # passed in subtopic IDs, if they don't exist, which would be the # case if the creator deletes subtopics after the learner has # loaded the topic viewer page. if subtopic.id in selected_subtopic_ids: selected_skill_ids.extend(subtopic.skill_ids) try: skills = skill_fetchers.get_multi_skills(selected_skill_ids) except Exception as e: raise self.PageNotFoundException(e) skill_ids_to_descriptions_map = {} for skill in skills: skill_ids_to_descriptions_map[skill.id] = skill.description self.values.update({ 'topic_name': topic.name, 'skill_ids_to_descriptions_map': skill_ids_to_descriptions_map }) self.render_json(self.values)
def get(self, topic_name): if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException # Topic cannot be None as an exception will be thrown from its decorator # if so. topic = topic_fetchers.get_topic_by_name(topic_name) comma_separated_subtopic_ids = self.request.get('selected_subtopic_ids') selected_subtopic_ids = comma_separated_subtopic_ids.split(',') selected_skill_ids = [] for subtopic in topic.subtopics: # An error is not thrown here, since it's fine to just ignore the # passed in subtopic IDs, if they don't exist, which would be the # case if the creator deletes subtopics after the learner has # loaded the topic viewer page. if python_utils.UNICODE(subtopic.id) in selected_subtopic_ids: selected_skill_ids.extend(subtopic.skill_ids) try: skills = skill_fetchers.get_multi_skills(selected_skill_ids) except Exception as e: raise self.PageNotFoundException(e) skill_ids_to_descriptions_map = {} for skill in skills: skill_ids_to_descriptions_map[skill.id] = skill.description self.values.update({ 'topic_name': topic.name, 'skill_ids_to_descriptions_map': skill_ids_to_descriptions_map }) self.render_json(self.values)
def get(self, topic_name, subtopic_id): """Handles GET requests. Args: topic_name: str. The name of the topic that the subtopic is present in. subtopic_id: str. The id of the subtopic, which is an integer in string form. """ if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException subtopic_id = int(subtopic_id) topic = topic_fetchers.get_topic_by_name(topic_name) next_subtopic_dict = None for index, subtopic in enumerate(topic.subtopics): if subtopic.id == subtopic_id: subtopic_title = subtopic.title if index != len(topic.subtopics) - 1: next_subtopic_dict = topic.subtopics[index + 1].to_dict() break subtopic_page_contents = ( subtopic_page_services.get_subtopic_page_contents_by_id( topic.id, subtopic_id)) subtopic_page_contents_dict = subtopic_page_contents.to_dict() self.values.update({ 'topic_id': topic.id, 'page_contents': subtopic_page_contents_dict, 'subtopic_title': subtopic_title, 'next_subtopic_dict': next_subtopic_dict }) 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 = skill_services.get_skill_descriptions_by_ids( topic.id, assigned_skill_ids) 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)
def test_initialize_topic_is_published(self): self.post_json('/initialize_android_test_data', {}, use_payload=False, csrf_token=None) self.assertTrue( topic_services.does_topic_with_name_exist('Android test')) topic = topic_fetchers.get_topic_by_name('Android test') topic_rights = topic_fetchers.get_topic_rights(topic.id, strict=False) self.assertTrue(topic_rights.topic_is_published)
def test_initialize_twice_raises_unpublished_topic_exception(self): self.post_json('/initialize_android_test_data', {}, use_payload=False, csrf_token=None) topic = topic_fetchers.get_topic_by_name('Android test') topic_services.unpublish_topic(topic.id, feconf.SYSTEM_COMMITTER_ID) response = self.post_json('/initialize_android_test_data', {}, use_payload=False, csrf_token=None, expected_status_int=400) self.assertEqual(response['error'], 'The topic exists but is not published.')
def get(self, page_context, page_identifier, asset_type, encoded_filename): """Returns an asset file. Args: page_context: str. The context of the page where the asset is required. page_identifier: str. The unique identifier for the particular context. Valid page_context: page_identifier pairs: exploration: exp_id story: story_id topic: topic_id skill: skill_id subtopic: topic_name of the topic that it is part of. asset_type: str. Type of the asset, either image or audio. encoded_filename: str. The asset filename. This string is encoded in the frontend using encodeURIComponent(). """ if not constants.DEV_MODE: raise self.PageNotFoundException try: filename = python_utils.urllib_unquote(encoded_filename) file_format = filename[(filename.rfind('.') + 1):] # If the following is not cast to str, an error occurs in the wsgi # library because unicode gets used. self.response.headers[ 'Content-Type'] = python_utils.convert_to_bytes( '%s/%s' % (asset_type, file_format)) if page_context == feconf.ENTITY_TYPE_SUBTOPIC: entity_type = feconf.ENTITY_TYPE_TOPIC topic = topic_fetchers.get_topic_by_name(page_identifier) entity_id = topic.id elif (page_context == feconf.ENTITY_TYPE_EXPLORATION or page_context == feconf.ENTITY_TYPE_SKILL or page_context == feconf.ENTITY_TYPE_TOPIC or page_context == feconf.ENTITY_TYPE_STORY): entity_type = page_context entity_id = page_identifier else: raise self.InvalidInputException fs = fs_domain.AbstractFileSystem( fs_domain.DatastoreBackedFileSystem(entity_type, entity_id)) raw = fs.get('%s/%s' % (asset_type, filename)) self.response.cache_control.no_cache = None self.response.cache_control.public = True self.response.cache_control.max_age = 600 self.response.write(raw) except: raise self.PageNotFoundException
def get(self, topic_name): if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException # Topic cannot be None as an exception will be thrown from its decorator # if so. topic = topic_fetchers.get_topic_by_name(topic_name) try: skills = skill_services.get_multi_skills(topic.get_all_skill_ids()) except Exception, e: raise self.PageNotFoundException(e)
def test_initialize_structure_thumbnails_exist(self): # To validate the thumbnails for topics ans stories can be fetched # using AssetsDevHandler. self.post_json('/initialize_android_test_data', {}, use_payload=False, csrf_token=None) topic = topic_fetchers.get_topic_by_name('Android test') story = story_fetchers.get_story_by_url_fragment( 'android-end-to-end-testing') self.get_custom_response( '/assetsdevhandler/topic/%s/assets/thumbnail/test_svg.svg' % topic.id, 'image/svg+xml') self.get_custom_response( '/assetsdevhandler/story/%s/assets/thumbnail/test_svg.svg' % story.id, 'image/svg+xml')
def does_topic_with_name_exist(topic_name): """Checks if the topic with provided name exists. Args: topic_name: str. The topic name. Returns: bool. Whether the the topic name exists. Raises: Exception. Topic name is not a string. """ if not isinstance(topic_name, python_utils.BASESTRING): raise utils.ValidationError('Name should be a string.') existing_topic = topic_fetchers.get_topic_by_name(topic_name) return existing_topic is not None
def test_initialize_structures_are_valid(self): self.post_json('/initialize_android_test_data', {}, use_payload=False, csrf_token=None) exp_id = '26' topic = topic_fetchers.get_topic_by_name('Android test') exploration = exp_fetchers.get_exploration_by_id(exp_id) story = story_fetchers.get_story_by_url_fragment( 'android-end-to-end-testing') skill = skill_fetchers.get_skill_by_description( 'Dummy Skill for Android') skill.validate() story.validate() topic.validate(strict=True) exploration.validate(strict=True) for node in story.story_contents.nodes: self.assertEqual(node.exploration_id, exp_id)
def get(self, target_type, suggestion_type): """Handles GET requests. Args: target_type: str. The type of the suggestion target. suggestion_type: str. The type of the suggestion. """ self._require_valid_suggestion_and_target_types( target_type, suggestion_type) limit = self.normalized_request.get('limit') offset = self.normalized_request.get('offset') topic_name = self.normalized_request.get('topic_name') opportunity_summary_exp_ids_specific_to_topic = None if (topic_name is not None) and (topic_name != feconf.ALL_LITERAL_CONSTANT): 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) exploration_opportunity_summaries = ( opportunity_services. get_exploration_opportunity_summaries_by_topic_id(topic.id)) opportunity_summary_exp_ids_specific_to_topic = [ opportunity.id for opportunity in exploration_opportunity_summaries ] suggestions = [] next_offset = 0 if suggestion_type == feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT: suggestions, next_offset = ( suggestion_services. get_reviewable_translation_suggestions_by_offset( self.user_id, opportunity_summary_exp_ids_specific_to_topic, limit, offset)) elif suggestion_type == feconf.SUGGESTION_TYPE_ADD_QUESTION: suggestions, next_offset = ( suggestion_services. get_reviewable_question_suggestions_by_offset( self.user_id, limit, offset)) self._render_suggestions(target_type, suggestions, next_offset)
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(self, topic_name): if not constants.ENABLE_NEW_STRUCTURE_PLAYERS: raise self.PageNotFoundException # Topic cannot be None as an exception will be thrown from its decorator # if so. topic = topic_fetchers.get_topic_by_name(topic_name) try: skills = skill_services.get_multi_skills(topic.get_all_skill_ids()) except Exception as e: raise self.PageNotFoundException(e) skill_descriptions = {} for skill in skills: skill_descriptions[skill.id] = skill.description self.values.update({ 'topic_name': topic.name, 'skill_descriptions': skill_descriptions }) self.render_json(self.values)
def save_new_topic(committer_id, topic): """Saves a new topic. Args: committer_id: str. ID of the committer. topic: Topic. Topic to be saved. Raises: Exception. Topic with same name already exists. """ existing_topic = topic_fetchers.get_topic_by_name(topic.name) if existing_topic is not None: raise Exception('Topic with name \'%s\' already exists' % topic.name) commit_message = ('New topic created with name \'%s\'.' % topic.name) _create_topic(committer_id, topic, commit_message, [ topic_domain.TopicChange({ 'cmd': topic_domain.CMD_CREATE_NEW, 'name': topic.name }) ])
def get(self, topic_name, subtopic_id): """Handles GET requests. Args: topic_name: str. The name of the topic that the subtopic is present in. subtopic_id: str. The id of the subtopic, which is an integer in string form. """ subtopic_id = int(subtopic_id) topic = topic_fetchers.get_topic_by_name(topic_name) next_subtopic_dict = None prev_subtopic_dict = None for index, subtopic in enumerate(topic.subtopics): if subtopic.id == subtopic_id: subtopic_title = subtopic.title if index != len(topic.subtopics) - 1: next_subtopic_dict = topic.subtopics[index + 1].to_dict() # Checking greater than 1 here, since otherwise the only # subtopic page of the topic would always link to itself at the # bottom of the subtopic page which isn't expected. elif len(topic.subtopics) > 1: prev_subtopic_dict = topic.subtopics[index - 1].to_dict() break subtopic_page_contents = ( subtopic_page_services.get_subtopic_page_contents_by_id( topic.id, subtopic_id)) subtopic_page_contents_dict = subtopic_page_contents.to_dict() self.values.update({ 'topic_id': topic.id, 'topic_name': topic.name, 'page_contents': subtopic_page_contents_dict, 'subtopic_title': subtopic_title, 'next_subtopic_dict': next_subtopic_dict, 'prev_subtopic_dict': prev_subtopic_dict, }) self.render_json(self.values)
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_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)
def test_get_topic_by_name(self) -> None: topic: Optional[topic_domain.Topic] = ( topic_fetchers.get_topic_by_name('Name')) # Ruling out the possibility of None for mypy type checking. assert topic is not None self.assertEqual(topic.name, 'Name')
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 post(self): """Generates structures for Android end-to-end tests. This handler generates structures for Android end-to-end tests in order to evaluate the integration of network requests from the Android client to the backend. This handler should only be called once (or otherwise raises an exception), and can only be used in development mode (this handler is unavailable in production). Note that the handler outputs an empty JSON dict when the request is successful. The specific structures that are generated: Topic: A topic with both a test story and a subtopic. Story: A story with 'android_interactions' as a exploration node. Exploration: 'android_interactions' from the local assets. Subtopic: A dummy subtopic to validate the topic. Skill: A dummy skill to validate the subtopic. Raises: Exception. When used in production mode. InvalidInputException. The topic is already created but not published. InvalidInputException. The topic is already published. """ if not constants.DEV_MODE: raise Exception('Cannot load new structures data in production.') if topic_services.does_topic_with_name_exist('Android test'): topic = topic_fetchers.get_topic_by_name('Android test') topic_rights = topic_fetchers.get_topic_rights(topic.id, strict=False) if topic_rights.topic_is_published: raise self.InvalidInputException( 'The topic is already published.') raise self.InvalidInputException( 'The topic exists but is not published.') exp_id = '26' user_id = feconf.SYSTEM_COMMITTER_ID # Generate new Structure id for topic, story, skill and question. topic_id = topic_fetchers.get_new_topic_id() story_id = story_services.get_new_story_id() skill_id = skill_services.get_new_skill_id() question_id = question_services.get_new_question_id() # Create dummy skill and question. skill = self._create_dummy_skill(skill_id, 'Dummy Skill for Android', '<p>Dummy Explanation 1</p>') question = self._create_dummy_question(question_id, 'Question 1', [skill_id]) question_services.add_question(user_id, question) question_services.create_new_question_skill_link( user_id, question_id, skill_id, 0.3) # Create and update topic to validate before publishing. topic = topic_domain.Topic.create_default_topic( topic_id, 'Android test', 'test-topic-one', 'description', 'fragm') topic.update_url_fragment('test-topic') topic.update_meta_tag_content('tag') topic.update_page_title_fragment_for_web('page title for topic') # Save the dummy image to the filesystem to be used as thumbnail. with utils.open_file(os.path.join(feconf.TESTS_DATA_DIR, 'test_svg.svg'), 'rb', encoding=None) as f: raw_image = f.read() fs = fs_services.GcsFileSystem(feconf.ENTITY_TYPE_TOPIC, topic_id) fs.commit('%s/test_svg.svg' % (constants.ASSET_TYPE_THUMBNAIL), raw_image, mimetype='image/svg+xml') # Update thumbnail properties. topic_services.update_thumbnail_filename(topic, 'test_svg.svg') topic.update_thumbnail_bg_color('#C6DCDA') # Add other structures to the topic. topic.add_canonical_story(story_id) topic.add_uncategorized_skill_id(skill_id) topic.add_subtopic(1, 'Test Subtopic Title', 'testsubtop') # Update and validate subtopic. topic_services.update_subtopic_thumbnail_filename( topic, 1, 'test_svg.svg') topic.update_subtopic_thumbnail_bg_color(1, '#FFFFFF') topic.update_subtopic_url_fragment(1, 'suburl') topic.move_skill_id_to_subtopic(None, 1, skill_id) subtopic_page = ( subtopic_page_domain.SubtopicPage.create_default_subtopic_page( 1, topic_id)) # Upload local exploration to the datastore and enable feedback. exp_services.load_demo(exp_id) rights_manager.release_ownership_of_exploration( user_services.get_system_user(), exp_id) exp_services.update_exploration( user_id, exp_id, [ exp_domain.ExplorationChange({ 'cmd': exp_domain.CMD_EDIT_EXPLORATION_PROPERTY, 'property_name': 'correctness_feedback_enabled', 'new_value': True }) ], 'Changed correctness_feedback_enabled.') # Add and update the exploration/node to the story. story = story_domain.Story.create_default_story( story_id, 'Android End to End testing', 'Description', topic_id, 'android-end-to-end-testing') story.add_node('%s%d' % (story_domain.NODE_ID_PREFIX, 1), 'Testing with UI Automator') story.update_node_description( '%s%d' % (story_domain.NODE_ID_PREFIX, 1), 'To test all Android interactions') story.update_node_exploration_id( '%s%d' % (story_domain.NODE_ID_PREFIX, 1), exp_id) # Save the dummy image to the filesystem to be used as thumbnail. with utils.open_file(os.path.join(feconf.TESTS_DATA_DIR, 'test_svg.svg'), 'rb', encoding=None) as f: raw_image = f.read() fs = fs_services.GcsFileSystem(feconf.ENTITY_TYPE_STORY, story_id) fs.commit('%s/test_svg.svg' % (constants.ASSET_TYPE_THUMBNAIL), raw_image, mimetype='image/svg+xml') story.update_node_thumbnail_filename( '%s%d' % (story_domain.NODE_ID_PREFIX, 1), 'test_svg.svg') story.update_node_thumbnail_bg_color( '%s%d' % (story_domain.NODE_ID_PREFIX, 1), '#F8BF74') # Update and validate the story. story.update_meta_tag_content('tag') story.update_thumbnail_filename('test_svg.svg') story.update_thumbnail_bg_color( constants.ALLOWED_THUMBNAIL_BG_COLORS['story'][0]) # Save the previously created structures # (skill, story, topic, subtopic). skill_services.save_new_skill(user_id, skill) story_services.save_new_story(user_id, story) topic_services.save_new_topic(user_id, topic) subtopic_page_services.save_subtopic_page( user_id, subtopic_page, 'Added subtopic', [ topic_domain.TopicChange({ 'cmd': topic_domain.CMD_ADD_SUBTOPIC, 'subtopic_id': 1, 'title': 'Dummy Subtopic Title', 'url_fragment': 'dummy-fragment' }) ]) # Generates translation opportunities for the Contributor Dashboard. exp_ids_in_story = story.story_contents.get_all_linked_exp_ids() opportunity_services.add_new_exploration_opportunities( story_id, exp_ids_in_story) # Publish the story and topic. topic_services.publish_story(topic_id, story_id, user_id) topic_services.publish_topic(topic_id, user_id) # Upload thumbnails to be accessible through AssetsDevHandler. self._upload_thumbnail(topic_id, feconf.ENTITY_TYPE_TOPIC) self._upload_thumbnail(story_id, feconf.ENTITY_TYPE_STORY) self.render_json({})
def test_get_topic_by_name(self): topic = topic_fetchers.get_topic_by_name('Name') self.assertEqual(topic.name, 'Name')