def test_migration_job_does_not_convert_up_to_date_story(self): """Tests that the story migration job does not convert a story that is already the latest schema version. """ # Create a new story that should not be affected by the # job. story = story_domain.Story.create_default_story( self.STORY_ID, 'A title', 'Description', self.TOPIC_ID, 'title-one') story_services.save_new_story(self.albert_id, story) topic_services.add_canonical_story(self.albert_id, self.TOPIC_ID, story.id) self.assertEqual(story.story_contents_schema_version, feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION) # Start migration job. job_id = (story_jobs_one_off.StoryMigrationOneOffJob.create_new()) story_jobs_one_off.StoryMigrationOneOffJob.enqueue(job_id) self.process_and_flush_pending_mapreduce_tasks() # Verify the story is exactly the same after migration. updated_story = (story_fetchers.get_story_by_id(self.STORY_ID)) self.assertEqual(updated_story.story_contents_schema_version, feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION) output = story_jobs_one_off.StoryMigrationOneOffJob.get_output(job_id) # pylint: disable=line-too-long expected = [[u'story_migrated', [u'1 stories successfully migrated.']]] self.assertEqual(expected, [ast.literal_eval(x) for x in output])
def test_migration_job_converts_old_story(self): """Tests that the schema conversion functions work correctly and an old story is converted to new version. """ # Generate story with old(v1) story contents data. self.save_new_story_with_story_contents_schema_v1( self.STORY_ID, self.albert_id, 'A title', 'A description', 'A note', self.TOPIC_ID) topic_services.add_canonical_story( self.albert_id, self.TOPIC_ID, self.STORY_ID) story = ( story_fetchers.get_story_by_id(self.STORY_ID)) self.assertEqual(story.story_contents_schema_version, 1) # Start migration job. job_id = ( story_jobs_one_off.StoryMigrationOneOffJob.create_new()) story_jobs_one_off.StoryMigrationOneOffJob.enqueue(job_id) self.process_and_flush_pending_tasks() # Verify the story migrates correctly. updated_story = ( story_fetchers.get_story_by_id(self.STORY_ID)) self.assertEqual( updated_story.story_contents_schema_version, feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION) output = story_jobs_one_off.StoryMigrationOneOffJob.get_output(job_id) # pylint: disable=line-too-long expected = [[u'story_migrated', [u'1 stories successfully migrated.']]] self.assertEqual(expected, [ast.literal_eval(x) for x in output])
def test_regeneration_job_skips_invalid_story(self): def _mock_get_story_by_id(unused_story_id): """Mocks get_story_by_id().""" return 'invalid_story' story = story_domain.Story.create_default_story( self.STORY_ID, 'A title', 'Description', self.TOPIC_ID, 'title-five') story_services.save_new_story(self.albert_id, story) topic_services.add_canonical_story(self.albert_id, self.TOPIC_ID, story.id) get_story_by_id_swap = self.swap(story_fetchers, 'get_story_by_id', _mock_get_story_by_id) with get_story_by_id_swap: job_id = (story_jobs_one_off.RegenerateStorySummaryOneOffJob. create_new()) story_jobs_one_off.RegenerateStorySummaryOneOffJob.enqueue(job_id) self.process_and_flush_pending_mapreduce_tasks() output = story_jobs_one_off.RegenerateStorySummaryOneOffJob.get_output( job_id) for x in output: self.assertRegexpMatches( x, 'object has no attribute \'story_contents\'')
def test_job_skips_deleted_story(self): """Tests that the regenerate summary job skips deleted story.""" story = story_domain.Story.create_default_story( self.STORY_ID, 'A title', 'Description', self.TOPIC_ID, 'title-four') story_services.save_new_story(self.albert_id, story) topic_services.add_canonical_story(self.albert_id, self.TOPIC_ID, story.id) story_services.delete_story(self.albert_id, self.STORY_ID) # Ensure the story is deleted. with self.assertRaisesRegexp(Exception, 'Entity .* not found'): story_fetchers.get_story_by_id(self.STORY_ID) # Start migration job on sample story. job_id = ( story_jobs_one_off.RegenerateStorySummaryOneOffJob.create_new()) story_jobs_one_off.RegenerateStorySummaryOneOffJob.enqueue(job_id) # This running without errors indicates the deleted story is # being ignored. self.process_and_flush_pending_mapreduce_tasks() # Ensure the story is still deleted. with self.assertRaisesRegexp(Exception, 'Entity .* not found'): story_fetchers.get_story_by_id(self.STORY_ID) output = story_jobs_one_off.RegenerateStorySummaryOneOffJob.get_output( job_id) expected = [[u'story_deleted', [u'Encountered 1 deleted stories.']]] self.assertEqual(expected, [ast.literal_eval(x) for x in output])
def test_migration_job_skips_updated_story_failing_validation(self): story = story_domain.Story.create_default_story( self.STORY_ID, 'A title', 'Description', self.TOPIC_ID, 'title-three') story_services.save_new_story(self.albert_id, story) topic_services.add_canonical_story(self.albert_id, self.TOPIC_ID, story.id) story.description = 123 def _mock_get_story_by_id(unused_story_id): """Mocks get_story_by_id().""" return story get_story_by_id_swap = self.swap(story_fetchers, 'get_story_by_id', _mock_get_story_by_id) with get_story_by_id_swap: job_id = (story_jobs_one_off.StoryMigrationOneOffJob.create_new()) story_jobs_one_off.StoryMigrationOneOffJob.enqueue(job_id) self.process_and_flush_pending_mapreduce_tasks() output = story_jobs_one_off.StoryMigrationOneOffJob.get_output(job_id) # If the story had been successfully migrated, this would include a # 'successfully migrated' message. Its absence means that the story # could not be processed. for x in output: self.assertRegexpMatches( x, 'Expected description to be a string, received 123')
def setUp(self): super(OpportunityServicesIntegerationTest, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.TOPIC_ID = 'topic' self.STORY_ID = 'story' explorations = [ exp_domain.Exploration.create_default_exploration( '%s' % i, title='title %d' % i, category='category%d' % i, ) for i in python_utils.RANGE(5) ] for exp in explorations: exp_services.save_new_exploration(self.owner_id, exp) topic = topic_domain.Topic.create_default_topic(topic_id=self.TOPIC_ID, name='topic') topic_services.save_new_topic(self.owner_id, topic) story = story_domain.Story.create_default_story( self.STORY_ID, title='A story', corresponding_topic_id=self.TOPIC_ID) story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, self.TOPIC_ID, self.STORY_ID)
def test_migration_job_converts_old_story(self): """Tests that the schema conversion functions work correctly and an old story is converted to new version. """ # Generate story with old(v1) story contents data. self.save_new_story_with_story_contents_schema_v1( self.STORY_ID, 'image.svg', '#F8BF74', self.albert_id, 'A title', 'A description', 'A note', self.TOPIC_ID) topic_services.add_canonical_story(self.albert_id, self.TOPIC_ID, self.STORY_ID) story_model = (story_models.StoryModel.get(self.STORY_ID)) self.assertEqual(story_model.story_contents_schema_version, 1) self.assertEqual( story_model.story_contents, { 'initial_node_id': 'node_1', 'next_node_id': 'node_2', 'nodes': [{ 'acquired_skill_ids': [], 'destination_node_ids': [], 'exploration_id': None, 'id': 'node_1', 'outline': ('<p>Value</p><oppia-noninteractive-math raw_l' 'atex-with-value="&quot;+,-,-,+&quot' ';"></oppia-noninteractive-math>'), 'outline_is_finalized': False, 'prerequisite_skill_ids': [], 'title': 'Chapter 1' }] }) story = story_fetchers.get_story_by_id(self.STORY_ID) self.assertEqual(story.story_contents_schema_version, 4) self.assertEqual(story.story_contents.to_dict(), self.MIGRATED_STORY_CONTENTS_DICT) # Start migration job. job_id = (story_jobs_one_off.StoryMigrationOneOffJob.create_new()) story_jobs_one_off.StoryMigrationOneOffJob.enqueue(job_id) self.process_and_flush_pending_tasks() # Verify the story migrates correctly. updated_story = (story_models.StoryModel.get(self.STORY_ID)) self.assertEqual(updated_story.story_contents_schema_version, feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION) updated_story = (story_fetchers.get_story_by_id(self.STORY_ID)) self.assertEqual(updated_story.story_contents_schema_version, feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION) self.assertEqual(updated_story.story_contents.to_dict(), self.MIGRATED_STORY_CONTENTS_DICT) output = story_jobs_one_off.StoryMigrationOneOffJob.get_output(job_id) expected = [[u'story_migrated', [u'1 stories successfully migrated.']]] self.assertEqual(expected, [ast.literal_eval(x) for x in output])
def setUp(self): super(OpportunityServicesUnitTest, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.signup(self.CURRICULUM_ADMIN_EMAIL, self.CURRICULUM_ADMIN_USERNAME) self.admin_id = self.get_user_id_from_email(self.CURRICULUM_ADMIN_EMAIL) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.set_curriculum_admins([self.CURRICULUM_ADMIN_USERNAME]) self.TOPIC_ID = 'topic' self.STORY_ID = 'story' explorations = [self.save_new_valid_exploration( '%s' % i, self.owner_id, title='title %d' % i, category='category%d' % i, end_state_name='End State', correctness_feedback_enabled=True ) for i in python_utils.RANGE(5)] for exp in explorations: self.publish_exploration(self.owner_id, exp.id) topic = topic_domain.Topic.create_default_topic( self.TOPIC_ID, 'topic', 'abbrev', 'description') topic.thumbnail_filename = 'thumbnail.svg' topic.thumbnail_bg_color = '#C6DCDA' topic.subtopics = [ topic_domain.Subtopic( 1, 'Title', ['skill_id_1'], 'image.svg', constants.ALLOWED_THUMBNAIL_BG_COLORS['subtopic'][0], 21131, 'dummy-subtopic-url')] topic.next_subtopic_id = 2 topic_services.save_new_topic(self.owner_id, topic) topic_services.publish_topic(self.TOPIC_ID, self.admin_id) story = story_domain.Story.create_default_story( self.STORY_ID, 'A story', 'Description', self.TOPIC_ID, 'story-two') story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story( self.owner_id, self.TOPIC_ID, self.STORY_ID) topic_services.publish_story( self.TOPIC_ID, self.STORY_ID, self.admin_id) story_services.update_story( self.owner_id, self.STORY_ID, [story_domain.StoryChange({ 'cmd': 'add_story_node', 'node_id': 'node_1', 'title': 'Node1', }), story_domain.StoryChange({ 'cmd': 'update_story_node_property', 'property_name': 'exploration_id', 'node_id': 'node_1', 'old_value': None, 'new_value': '0' })], 'Changes.')
def setUp(self): super(VoiceoverApplicationServicesUnitTests, self).setUp() self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.signup(self.APPLICANT_EMAIL, self.APPLICANT_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.applicant_id = self.get_user_id_from_email(self.APPLICANT_EMAIL) self.applicant = user_services.UserActionsInfo(self.applicant_id) self.set_admins([self.ADMIN_USERNAME]) self.admin = user_services.UserActionsInfo(self.admin_id) self.TOPIC_ID = 'topic' self.STORY_ID = 'story' self.USER_ID = 'user' self.SKILL_ID = 'skill' self.QUESTION_ID = question_services.get_new_question_id() explorations = [ self.save_new_valid_exploration('%s' % i, self.owner_id, title='title %d' % i, category='category%d' % i, end_state_name='End State') for i in python_utils.RANGE(2) ] for exp in explorations: self.publish_exploration(self.owner_id, exp.id) topic = topic_domain.Topic.create_default_topic( self.TOPIC_ID, 'topic', 'abbrev', 'description') topic_services.save_new_topic(self.owner_id, topic) story = story_domain.Story.create_default_story( self.STORY_ID, title='A story', corresponding_topic_id=self.TOPIC_ID) story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, self.TOPIC_ID, self.STORY_ID) story_services.update_story(self.owner_id, self.STORY_ID, [ story_domain.StoryChange({ 'cmd': 'add_story_node', 'node_id': 'node_1', 'title': 'Node1', }), story_domain.StoryChange({ 'cmd': 'update_story_node_property', 'property_name': 'exploration_id', 'node_id': 'node_1', 'old_value': None, 'new_value': '0' }) ], 'Changes.')
def setUp(self): super(TranslatableTextHandlerTest, self).setUp() self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.set_admins([self.ADMIN_USERNAME]) explorations = [ self.save_new_valid_exploration('%s' % i, self.owner_id, title='title %d' % i, category='category%d' % i, end_state_name='End State') for i in python_utils.RANGE(2) ] for exp in explorations: self.publish_exploration(self.owner_id, exp.id) topic = topic_domain.Topic.create_default_topic( '0', 'topic', 'abbrev', 'description') topic.thumbnail_filename = 'thumbnail.svg' topic.thumbnail_bg_color = '#C6DCDA' topic_services.save_new_topic(self.owner_id, topic) topic_services.publish_topic(topic.id, self.admin_id) stories = [ story_domain.Story.create_default_story('%s' % i, 'title %d' % i, 'description %d' % i, '0', 'title-%s' % chr(97 + i)) for i in python_utils.RANGE(2) ] for index, story in enumerate(stories): story.language_code = 'en' story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, topic.id, story.id) topic_services.publish_story(topic.id, story.id, self.admin_id) story_services.update_story(self.owner_id, story.id, [ story_domain.StoryChange({ 'cmd': 'add_story_node', 'node_id': 'node_1', 'title': 'Node1', }), story_domain.StoryChange({ 'cmd': 'update_story_node_property', 'property_name': 'exploration_id', 'node_id': 'node_1', 'old_value': None, 'new_value': explorations[index].id }) ], 'Changes.')
def post(self, topic_id): """Handles POST requests. Currently, this only adds the story to the canonical story id list of the topic. """ title = self.payload.get('title') description = self.payload.get('description') thumbnail_filename = self.payload.get('filename') thumbnail_bg_color = self.payload.get('thumbnailBgColor') raw_image = self.request.get('image') story_url_fragment = self.payload.get('story_url_fragment') story_domain.Story.require_valid_title(title) if story_services.does_story_exist_with_url_fragment( story_url_fragment): raise self.InvalidInputException( 'Story url fragment is not unique across the site.') new_story_id = story_services.get_new_story_id() story = story_domain.Story.create_default_story( new_story_id, title, description, topic_id, story_url_fragment) story_services.save_new_story(self.user_id, story) topic_services.add_canonical_story(self.user_id, topic_id, new_story_id) try: file_format = image_validation_services.validate_image_and_filename( raw_image, thumbnail_filename) except utils.ValidationError as e: raise self.InvalidInputException(e) entity_id = new_story_id filename_prefix = 'thumbnail' image_is_compressible = (file_format in feconf.COMPRESSIBLE_IMAGE_FORMATS) fs_services.save_original_and_compressed_versions_of_image( thumbnail_filename, feconf.ENTITY_TYPE_STORY, entity_id, raw_image, filename_prefix, image_is_compressible) story_services.update_story(self.user_id, new_story_id, [ story_domain.StoryChange({ 'cmd': 'update_story_property', 'property_name': 'thumbnail_filename', 'old_value': None, 'new_value': thumbnail_filename }), story_domain.StoryChange({ 'cmd': 'update_story_property', 'property_name': 'thumbnail_bg_color', 'old_value': None, 'new_value': thumbnail_bg_color }), ], 'Added story thumbnail.') self.render_json({'storyId': new_story_id})
def setUp(self) -> None: super(StoryFetchersUnitTests, self).setUp() self.story_id = story_services.get_new_story_id() self.TOPIC_ID = topic_fetchers.get_new_topic_id( ) # type: ignore[no-untyped-call] self.save_new_topic( # type: ignore[no-untyped-call] self.TOPIC_ID, self.USER_ID, name='Topic', description='A new topic', canonical_story_ids=[], additional_story_ids=[], uncategorized_skill_ids=[], subtopics=[], next_subtopic_id=0) self.save_new_story( # type: ignore[no-untyped-call] self.story_id, self.USER_ID, self.TOPIC_ID, url_fragment='story-one') topic_services.add_canonical_story( # type: ignore[no-untyped-call] self.USER_ID, self.TOPIC_ID, self.story_id) changelist = [ story_domain.StoryChange({ 'cmd': story_domain.CMD_ADD_STORY_NODE, 'node_id': self.NODE_ID_1, 'title': 'Title 1' }) ] story_services.update_story( # type: ignore[no-untyped-call] self.USER_ID, self.story_id, changelist, 'Added node.') self.story = story_fetchers.get_story_by_id(self.story_id) self.signup('*****@*****.**', 'A') self.signup('*****@*****.**', 'B') self.signup(self.CURRICULUM_ADMIN_EMAIL, self.CURRICULUM_ADMIN_USERNAME) self.user_id_a = self.get_user_id_from_email( '*****@*****.**') # type: ignore[no-untyped-call] self.user_id_b = self.get_user_id_from_email( '*****@*****.**') # type: ignore[no-untyped-call] self.user_id_admin = (self.get_user_id_from_email( self.CURRICULUM_ADMIN_EMAIL)) # type: ignore[no-untyped-call] self.set_curriculum_admins([self.CURRICULUM_ADMIN_USERNAME ]) # type: ignore[no-untyped-call] self.set_topic_managers( # type: ignore[no-untyped-call] [user_services.get_username(self.user_id_a)], self.TOPIC_ID) # type: ignore[no-untyped-call] self.user_a = user_services.get_user_actions_info( self.user_id_a) # type: ignore[no-untyped-call] self.user_b = user_services.get_user_actions_info( self.user_id_b) # type: ignore[no-untyped-call] self.user_admin = user_services.get_user_actions_info( # type: ignore[no-untyped-call] self.user_id_admin)
def setUp(self): super(OpportunityServicesUnitTest, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.set_admins([self.ADMIN_USERNAME]) self.TOPIC_ID = 'topic' self.STORY_ID = 'story' explorations = [ self.save_new_valid_exploration('%s' % i, self.owner_id, title='title %d' % i, category='category%d' % i, end_state_name='End State') for i in python_utils.RANGE(5) ] for exp in explorations: self.publish_exploration(self.owner_id, exp.id) topic = topic_domain.Topic.create_default_topic( self.TOPIC_ID, 'topic', 'abbrev', 'description') topic.thumbnail_filename = 'thumbnail.svg' topic.thumbnail_bg_color = '#C6DCDA' topic_services.save_new_topic(self.owner_id, topic) topic_services.publish_topic(self.TOPIC_ID, self.admin_id) story = story_domain.Story.create_default_story( self.STORY_ID, 'A story', 'Description', self.TOPIC_ID, 'story-two') story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, self.TOPIC_ID, self.STORY_ID) topic_services.publish_story(self.TOPIC_ID, self.STORY_ID, self.admin_id) story_services.update_story(self.owner_id, self.STORY_ID, [ story_domain.StoryChange({ 'cmd': 'add_story_node', 'node_id': 'node_1', 'title': 'Node1', }), story_domain.StoryChange({ 'cmd': 'update_story_node_property', 'property_name': 'exploration_id', 'node_id': 'node_1', 'old_value': None, 'new_value': '0' }) ], 'Changes.')
def test_record_completed_node_in_story_context(self): # Ensure that node completed within the context of a story are # recorded correctly. This test actually validates both # test_get_completed_node_ids and # test_get_next_node_id_to_be_completed_by_user. # By default, no completion model should exist for a given user and # story. completion_model = self._get_progress_model(self.owner_id, self.STORY_1_ID) self.assertIsNone(completion_model) # If the user 'completes an node', the model should record it. story_services.record_completed_node_in_story_context( self.owner_id, self.STORY_1_ID, self.NODE_ID_1) completion_model = self._get_progress_model(self.owner_id, self.STORY_1_ID) self.assertIsNotNone(completion_model) self.assertEqual(completion_model.completed_node_ids, [self.NODE_ID_1]) # If the same node is completed again within the context of this # story, it should not be duplicated. story_services.record_completed_node_in_story_context( self.owner_id, self.STORY_1_ID, self.NODE_ID_1) completion_model = self._get_progress_model(self.owner_id, self.STORY_1_ID) self.assertEqual(completion_model.completed_node_ids, [self.NODE_ID_1]) # If the same node and another are completed within the context # of a different story, it shouldn't affect this one. self.story = self.save_new_story(self.STORY_ID_1, self.USER_ID, 'Title', 'Description', 'Notes', self.TOPIC_ID) topic_services.add_canonical_story(self.USER_ID, self.TOPIC_ID, self.STORY_ID_1) story_services.record_completed_node_in_story_context( self.owner_id, self.STORY_ID_1, self.NODE_ID_1) story_services.record_completed_node_in_story_context( self.owner_id, self.STORY_ID_1, self.NODE_ID_2) completion_model = self._get_progress_model(self.owner_id, self.STORY_1_ID) self.assertEqual(completion_model.completed_node_ids, [self.NODE_ID_1]) # If two more nodes are completed, they are recorded. story_services.record_completed_node_in_story_context( self.owner_id, self.STORY_1_ID, self.NODE_ID_2) story_services.record_completed_node_in_story_context( self.owner_id, self.STORY_1_ID, self.NODE_ID_3) completion_model = self._get_progress_model(self.owner_id, self.STORY_1_ID) self.assertEqual(completion_model.completed_node_ids, [self.NODE_ID_1, self.NODE_ID_2, self.NODE_ID_3])
def test_add_canonical_story(self): topic_services.add_canonical_story(self.user_id_admin, self.TOPIC_ID, 'story_id') topic = topic_services.get_topic_by_id(self.TOPIC_ID) self.assertEqual(topic.canonical_story_ids, [self.story_id_1, self.story_id_2, 'story_id']) topic_commit_log_entry = ( topic_models.TopicCommitLogEntryModel.get_commit(self.TOPIC_ID, 3)) self.assertEqual(topic_commit_log_entry.commit_type, 'edit') self.assertEqual(topic_commit_log_entry.topic_id, self.TOPIC_ID) self.assertEqual(topic_commit_log_entry.user_id, self.user_id_admin) self.assertEqual(topic_commit_log_entry.commit_message, 'Added %s to canonical story ids' % 'story_id')
def test_description_length_limit(self): """Checks description length.""" # Creating a new story. story = story_domain.Story.create_default_story( self.STORY_ID, 'A title', self.DESCRIPTION, self.TOPIC_ID, 'title-one') story_services.save_new_story(self.albert_id, story) topic_services.add_canonical_story( self.albert_id, self.TOPIC_ID, story.id) output = self._run_one_off_job() self.assertEqual( [u'[u\'Topic Id: topic_id\', u"Story Id: [\'story_id\']"]'], output )
def setUp(self): super(OpportunityServicesIntegrationTest, self).setUp() self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.set_admins([self.ADMIN_USERNAME]) self.admin = user_services.UserActionsInfo(self.admin_id) self.TOPIC_ID = 'topic' self.STORY_ID = 'story' self.USER_ID = 'user' self.SKILL_ID = 'skill' self.QUESTION_ID = question_services.get_new_question_id() self.THREAD_ID = 'exploration.exp1.thread_1' # Since a valid exploration is created here, it has EndExploration # state as well, so the content in that has to be taken into account as # well when checking content_count in the tests. explorations = [ self.save_new_valid_exploration('%s' % i, self.owner_id, title='title %d' % i, category='category%d' % i, end_state_name='End State') for i in python_utils.RANGE(5) ] for exp in explorations: self.publish_exploration(self.owner_id, exp.id) topic = topic_domain.Topic.create_default_topic( self.TOPIC_ID, 'topic', 'abbrev', 'description') topic.thumbnail_filename = 'thumbnail.svg' topic.thumbnail_bg_color = '#C6DCDA' topic_services.save_new_topic(self.owner_id, topic) topic_services.publish_topic(self.TOPIC_ID, self.admin_id) story = story_domain.Story.create_default_story( self.STORY_ID, 'A story', 'description', self.TOPIC_ID, 'story-one') story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, self.TOPIC_ID, self.STORY_ID) topic_services.publish_story(self.TOPIC_ID, self.STORY_ID, self.admin_id)
def post(self, topic_id): """Handles POST requests. Currently, this only adds the story to the canonical story id list of the topic. """ title = self.payload.get('title') story_domain.Story.require_valid_title(title) new_story_id = story_services.get_new_story_id() story = story_domain.Story.create_default_story( new_story_id, title, topic_id) story_services.save_new_story(self.user_id, story) topic_services.add_canonical_story(self.user_id, topic_id, new_story_id) self.render_json({'storyId': new_story_id})
def test_can_see_all_topics(self): self.login(self.CURRICULUM_ADMIN_EMAIL, is_super_admin=True) response = self.get_json( feconf.LEARNER_DASHBOARD_TOPIC_AND_STORY_DATA_URL) self.assertEqual(len(response['all_topics_list']), 0) self.save_new_topic(self.TOPIC_ID_1, self.owner_id, name=self.TOPIC_NAME_1, url_fragment='topic-one', description='A new topic', canonical_story_ids=[], additional_story_ids=[], uncategorized_skill_ids=[], subtopics=[self.subtopic_0], next_subtopic_id=1) topic_services.publish_topic(self.TOPIC_ID_1, self.admin_id) self.save_new_story(self.STORY_ID_2, self.owner_id, self.TOPIC_ID_1) topic_services.add_canonical_story(self.owner_id, self.TOPIC_ID_1, self.STORY_ID_2) topic_services.publish_story(self.TOPIC_ID_1, self.STORY_ID_2, self.admin_id) csrf_token = self.get_new_csrf_token() new_config_value = [{ 'name': 'math', 'url_fragment': 'math', 'topic_ids': [self.TOPIC_ID_1], 'course_details': '', 'topic_list_intro': '' }] payload = { 'action': 'save_config_properties', 'new_config_property_values': { config_domain.CLASSROOM_PAGES_DATA.name: (new_config_value), } } self.post_json('/adminhandler', payload, csrf_token=csrf_token) self.logout() self.login(self.VIEWER_EMAIL) response = self.get_json( feconf.LEARNER_DASHBOARD_TOPIC_AND_STORY_DATA_URL) self.assertEqual(len(response['all_topics_list']), 1) self.assertEqual(response['all_topics_list'][0]['id'], self.TOPIC_ID_1) self.logout()
def setUp(self): super(TranslatableTextHandlerTest, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) explorations = [ exp_domain.Exploration.create_default_exploration( '%s' % i, title='title %d' % i, category='category%d' % i, ) for i in python_utils.RANGE(2) ] for exp in explorations: exp_services.save_new_exploration(self.owner_id, exp) topic = topic_domain.Topic.create_default_topic( topic_id='0', name='topic', abbreviated_name='abbrev') topic_services.save_new_topic(self.owner_id, topic) stories = [ story_domain.Story.create_default_story('%s' % i, title='title %d' % i, corresponding_topic_id='0') for i in python_utils.RANGE(2) ] for index, story in enumerate(stories): story.language_code = 'en' story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, topic.id, story.id) story_services.update_story(self.owner_id, story.id, [ story_domain.StoryChange({ 'cmd': 'add_story_node', 'node_id': 'node_1', 'title': 'Node1', }), story_domain.StoryChange({ 'cmd': 'update_story_node_property', 'property_name': 'exploration_id', 'node_id': 'node_1', 'old_value': None, 'new_value': explorations[index].id }) ], 'Changes.')
def setUp(self): super(OpportunityServicesUnitTest, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.TOPIC_ID = 'topic' self.STORY_ID = 'story' explorations = [ self.save_new_valid_exploration('%s' % i, self.owner_id, title='title %d' % i, category='category%d' % i, end_state_name='End State') for i in python_utils.RANGE(5) ] for exp in explorations: self.publish_exploration(self.owner_id, exp.id) topic = topic_domain.Topic.create_default_topic( topic_id=self.TOPIC_ID, name='topic', abbreviated_name='abbrev') topic_services.save_new_topic(self.owner_id, topic) story = story_domain.Story.create_default_story( self.STORY_ID, title='A story', corresponding_topic_id=self.TOPIC_ID) story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, self.TOPIC_ID, self.STORY_ID) story_services.update_story(self.owner_id, self.STORY_ID, [ story_domain.StoryChange({ 'cmd': 'add_story_node', 'node_id': 'node_1', 'title': 'Node1', }), story_domain.StoryChange({ 'cmd': 'update_story_node_property', 'property_name': 'exploration_id', 'node_id': 'node_1', 'old_value': None, 'new_value': '0' }) ], 'Changes.')
def test_publish_and_unpublish_story(self): self.save_new_story('public_story', self.USER_ID, 'Title', 'Description', 'Notes', self.TOPIC_ID) topic_services.add_canonical_story(self.USER_ID, self.TOPIC_ID, 'public_story') changelist = [ story_domain.StoryChange({ 'cmd': story_domain.CMD_ADD_STORY_NODE, 'node_id': self.NODE_ID_1, 'title': 'Title 1' }) ] story_services.update_story(self.USER_ID, 'public_story', changelist, 'Added node.') self.save_new_default_exploration('exp_id', self.user_id_a, title='title') self.publish_exploration(self.user_id_a, 'exp_id') change_list = [ story_domain.StoryChange({ 'cmd': story_domain.CMD_UPDATE_STORY_NODE_PROPERTY, 'property_name': (story_domain.STORY_NODE_PROPERTY_EXPLORATION_ID), 'node_id': 'node_1', 'old_value': None, 'new_value': 'exp_id' }) ] story_services.update_story(self.USER_ID, 'public_story', change_list, 'Updated story node.') story_rights = story_services.get_story_rights('public_story') self.assertFalse(story_rights.story_is_published) story_services.publish_story('public_story', self.user_id_admin) with self.assertRaisesRegexp( Exception, 'The user does not have enough rights to unpublish the story.' ): story_services.unpublish_story('public_story', self.user_id_a)
def setUp(self): super(StoryServicesUnitTests, self).setUp() self.STORY_ID = story_services.get_new_story_id() self.TOPIC_ID = topic_services.get_new_topic_id() self.save_new_topic(self.TOPIC_ID, self.USER_ID, name='Topic', abbreviated_name='abbrev', thumbnail_filename=None, description='A new topic', canonical_story_ids=[], additional_story_ids=[], uncategorized_skill_ids=[], subtopics=[], next_subtopic_id=0) self.save_new_story(self.STORY_ID, self.USER_ID, 'Title', 'Description', 'Notes', self.TOPIC_ID) topic_services.add_canonical_story(self.USER_ID, self.TOPIC_ID, self.STORY_ID) changelist = [ story_domain.StoryChange({ 'cmd': story_domain.CMD_ADD_STORY_NODE, 'node_id': self.NODE_ID_1, 'title': 'Title 1' }) ] story_services.update_story(self.USER_ID, self.STORY_ID, changelist, 'Added node.') self.story = story_fetchers.get_story_by_id(self.STORY_ID) self.signup('*****@*****.**', 'A') self.signup('*****@*****.**', 'B') self.signup(self.ADMIN_EMAIL, username=self.ADMIN_USERNAME) self.user_id_a = self.get_user_id_from_email('*****@*****.**') self.user_id_b = self.get_user_id_from_email('*****@*****.**') self.user_id_admin = self.get_user_id_from_email(self.ADMIN_EMAIL) self.set_admins([self.ADMIN_USERNAME]) self.set_topic_managers([user_services.get_username(self.user_id_a)]) self.user_a = user_services.UserActionsInfo(self.user_id_a) self.user_b = user_services.UserActionsInfo(self.user_id_b) self.user_admin = user_services.UserActionsInfo(self.user_id_admin)
def test_report_skipped_stories(self): """Tests that the audit job skips stories which have a valid reference in the corresponding topic. """ story = story_domain.Story.create_default_story( self.STORY_ID, 'A title', 'Description', self.TOPIC_ID, 'title-nine') story_services.save_new_story(self.albert_id, story) topic_services.add_canonical_story(self.albert_id, self.TOPIC_ID, story.id) self.process_and_flush_pending_mapreduce_tasks() # Start migration job. job_id = (story_jobs_one_off.OrphanStoriesAuditJob.create_new()) story_jobs_one_off.OrphanStoriesAuditJob.enqueue(job_id) self.process_and_flush_pending_mapreduce_tasks() output = (story_jobs_one_off.OrphanStoriesAuditJob.get_output(job_id)) expected = [[u'story_skipped', [u'Skipped 1 valid stories.']]] self.assertEqual(expected, [ast.literal_eval(x) for x in output])
def setUp(self): super(DeleteStoryCommitLogsOneOffJobTests, self).setUp() # Setup user who will own the test story. self.signup(self.ALBERT_EMAIL, self.ALBERT_NAME) self.albert_id = self.get_user_id_from_email(self.ALBERT_EMAIL) self.TOPIC_ID = 'topic_id' self.save_new_topic( self.TOPIC_ID, self.albert_id, name='Name', description='Description', canonical_story_ids=[], additional_story_ids=[], uncategorized_skill_ids=[], subtopics=[], next_subtopic_id=1) story = story_domain.Story.create_default_story( self.STORY_ID, 'A title', 'DESCRIPTION', self.TOPIC_ID, 'title-one') story_services.save_new_story(self.albert_id, story) topic_services.add_canonical_story( self.albert_id, self.TOPIC_ID, story.id) self.process_and_flush_pending_mapreduce_tasks()
def test_thumbnail_size_job_thumbnail_size_is_present(self): self.save_new_story_with_story_contents_schema_v1( self.STORY_ID, 'image.svg', '#F8BF74', 21131, self.albert_id, 'A title', 'A description', 'A note', self.TOPIC_ID) topic_services.add_canonical_story(self.albert_id, self.TOPIC_ID, self.STORY_ID) story_model = story_models.StoryModel.get(self.STORY_ID) story = story_fetchers.get_story_from_model(story_model) self.assertEqual(21131, story.thumbnail_size_in_bytes) # Start migration job. job_id = (story_jobs_one_off.PopulateStoryThumbnailSizeOneOffJob. create_new()) story_jobs_one_off.PopulateStoryThumbnailSizeOneOffJob.enqueue(job_id) self.process_and_flush_pending_mapreduce_tasks() output = (story_jobs_one_off.PopulateStoryThumbnailSizeOneOffJob. get_output(job_id)) expected = [[u'thumbnail_size_already_exists', 1]] self.assertEqual(expected, [ast.literal_eval(x) for x in output])
def test_thumbnail_size_job_thumbnail_size_updated_for_old_story(self): """Tests that PopulateStoryThumbnailSizeOneOffJob job results in existing update success key when the thumbnail_size_in_bytes is to be updated. """ self.save_new_story_with_story_contents_schema_v1( self.STORY_ID, 'image.svg', '#F8BF74', 21131, self.albert_id, 'A title', 'A description', 'A note', self.TOPIC_ID) topic_services.add_canonical_story(self.albert_id, self.TOPIC_ID, self.STORY_ID) # Start migration job. job_id = (story_jobs_one_off.PopulateStoryThumbnailSizeOneOffJob. create_new()) story_jobs_one_off.PopulateStoryThumbnailSizeOneOffJob.enqueue(job_id) self.process_and_flush_pending_mapreduce_tasks() output = (story_jobs_one_off.PopulateStoryThumbnailSizeOneOffJob. get_output(job_id)) expected = [[u'thumbnail_size_already_exists', 1]] self.assertEqual(expected, [ast.literal_eval(x) for x in output])
def setUp(self): super(OpportunityServicesIntegrationTest, self).setUp() self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.set_admins([self.ADMIN_USERNAME]) self.admin = user_services.UserActionsInfo(self.admin_id) self.TOPIC_ID = 'topic' self.STORY_ID = 'story' self.USER_ID = 'user' self.SKILL_ID = 'skill' self.QUESTION_ID = question_services.get_new_question_id() explorations = [ exp_domain.Exploration.create_default_exploration( '%s' % i, title='title %d' % i, category='category%d' % i, ) for i in python_utils.RANGE(5) ] for exp in explorations: exp_services.save_new_exploration(self.owner_id, exp) topic = topic_domain.Topic.create_default_topic( topic_id=self.TOPIC_ID, name='topic', abbreviated_name='abbrev') topic_services.save_new_topic(self.owner_id, topic) story = story_domain.Story.create_default_story( self.STORY_ID, title='A story', corresponding_topic_id=self.TOPIC_ID) story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, self.TOPIC_ID, self.STORY_ID)
def post(self, topic_id): """Handles POST requests. Currently, this only adds the story to the canonical story id list of the topic. """ if not feconf.ENABLE_NEW_STRUCTURES: raise self.PageNotFoundException topic_domain.Topic.require_valid_topic_id(topic_id) title = self.payload.get('title') topic = topic_services.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.')) story_domain.Story.require_valid_title(title) new_story_id = story_services.get_new_story_id() story = story_domain.Story.create_default_story(new_story_id, title=title) story_services.save_new_story(self.user_id, story) topic_services.add_canonical_story(self.user_id, topic_id, new_story_id) self.render_json({'storyId': new_story_id})
def setUp(self): super(OpportunityServicesIntegrationTest, self).setUp() self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME) self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.set_admins([self.ADMIN_USERNAME]) self.admin = user_services.UserActionsInfo(self.admin_id) self.TOPIC_ID = 'topic' self.STORY_ID = 'story' self.USER_ID = 'user' self.SKILL_ID = 'skill' self.QUESTION_ID = question_services.get_new_question_id() self.THREAD_ID = 'exploration.exp1.thread_1' # Since a valid exploration is created here, it has EndExploration # state as well, so the content in that has to be taken into account as # well when checking content_count in the tests. explorations = [ self.save_new_valid_exploration('%s' % i, self.owner_id, title='title %d' % i, category='category%d' % i, end_state_name='End State', correctness_feedback_enabled=True) for i in python_utils.RANGE(5) ] for exp in explorations: self.publish_exploration(self.owner_id, exp.id) topic = topic_domain.Topic.create_default_topic( self.TOPIC_ID, 'topic', 'abbrev', 'description') topic.thumbnail_filename = 'thumbnail.svg' topic.thumbnail_bg_color = '#C6DCDA' topic.subtopics = [ topic_domain.Subtopic( 1, 'Title', ['skill_id_1'], 'image.svg', constants.ALLOWED_THUMBNAIL_BG_COLORS['subtopic'][0], 'dummy-subtopic-url') ] topic.next_subtopic_id = 2 subtopic_page = ( subtopic_page_domain.SubtopicPage.create_default_subtopic_page( 1, self.TOPIC_ID)) subtopic_page_services.save_subtopic_page( self.owner_id, subtopic_page, 'Added subtopic', [ topic_domain.TopicChange({ 'cmd': topic_domain.CMD_ADD_SUBTOPIC, 'subtopic_id': 1, 'title': 'Sample' }) ]) topic_services.save_new_topic(self.owner_id, topic) topic_services.publish_topic(self.TOPIC_ID, self.admin_id) story = story_domain.Story.create_default_story( self.STORY_ID, 'A story', 'description', self.TOPIC_ID, 'story-one') story_services.save_new_story(self.owner_id, story) topic_services.add_canonical_story(self.owner_id, self.TOPIC_ID, self.STORY_ID) topic_services.publish_story(self.TOPIC_ID, self.STORY_ID, self.admin_id)