Exemple #1
0
    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])
Exemple #2
0
    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])
Exemple #3
0
    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\'')
Exemple #4
0
    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])
Exemple #5
0
    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')
Exemple #6
0
    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="&amp;quot;+,-,-,+&amp;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])
Exemple #8
0
    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.')
Exemple #9
0
    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.')
Exemple #11
0
    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})
Exemple #12
0
    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)
Exemple #13
0
    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])
Exemple #15
0
 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
        )
Exemple #17
0
    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)
Exemple #18
0
    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()
Exemple #20
0
    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.')
Exemple #21
0
    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)
Exemple #23
0
    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])
Exemple #28
0
    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)
Exemple #29
0
    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)