Exemplo n.º 1
0
    def test_cannot_get_story_from_model_with_invalid_schema_version(self):
        story_model = story_models.StoryModel.get(self.STORY_ID)
        story_model.story_contents_schema_version = 0
        story_model.commit(self.USER_ID, 'change schema version', [])

        with self.assertRaisesRegexp(
                Exception,
                'Sorry, we can only process v1-v%d story schemas at '
                'present.' % feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION):
            story_fetchers.get_story_from_model(story_model)
Exemplo n.º 2
0
    def test_migrate_story_contents_to_latest_schema(self):
        story_id = story_services.get_new_story_id()
        topic_id = topic_services.get_new_topic_id()
        user_id = 'user_id'
        self.save_new_topic(topic_id,
                            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(story_id, user_id, 'Title', 'Description', 'Notes',
                            topic_id)

        story_model = story_models.StoryModel.get(story_id)
        self.assertEqual(story_model.story_contents_schema_version, 1)

        swap_story_object = self.swap(story_domain, 'Story', MockStoryObject)
        current_schema_version_swap = self.swap(
            feconf, 'CURRENT_STORY_CONTENTS_SCHEMA_VERSION', 2)

        with swap_story_object, current_schema_version_swap:
            story = story_fetchers.get_story_from_model(story_model)

        self.assertEqual(story.story_contents_schema_version, 2)
Exemplo n.º 3
0
def delete_story(committer_id, story_id, force_deletion=False):
    """Deletes the story with the given story_id.

    Args:
        committer_id: str. ID of the committer.
        story_id: str. ID of the story to be deleted.
        force_deletion: bool. If true, the story and its history are fully
            deleted and are unrecoverable. Otherwise, the story and all
            its history are marked as deleted, but the corresponding models are
            still retained in the datastore. This last option is the preferred
            one.
    """
    story_model = story_models.StoryModel.get(story_id)
    story = story_fetchers.get_story_from_model(story_model)
    exp_ids = story.story_contents.get_all_linked_exp_ids()
    story_model.delete(committer_id,
                       feconf.COMMIT_MESSAGE_STORY_DELETED,
                       force_deletion=force_deletion)

    # This must come after the story is retrieved. Otherwise the memcache
    # key will be reinstated.
    story_memcache_key = story_fetchers.get_story_memcache_key(story_id)
    memcache_services.delete(story_memcache_key)

    # Delete the summary of the story (regardless of whether
    # force_deletion is True or not).
    delete_story_summary(story_id)

    # Delete the opportunities available related to the exploration used in the
    # story.
    opportunity_services.delete_exploration_opportunities(exp_ids)
Exemplo n.º 4
0
    def test_thumbnail_size_job_thumbnail_size_is_not_present(self):
        self.save_new_story_with_story_contents_schema_v1(
            self.STORY_ID, 'image.svg', '#F8BF74', None, 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(None, 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_update_error',
            [
                u'Thumbnail image.svg for story story_id not'
                ' found on the filesystem'
            ]
        ]]
        self.assertEqual(expected, [ast.literal_eval(x) for x in output])
Exemplo n.º 5
0
    def _migrate_story(
        story_id: str,
        story_model: story_models.StoryModel,
        # This must have a default value of None. Otherwise, Beam won't
        # execute this code.
        topic_id_to_topic: Optional[Dict[str, topic_domain.Topic]] = None
    ) -> result.Result[Tuple[str, story_domain.Story], Tuple[str, Exception]]:
        """Migrates story and transform story model into story object.

        Args:
            story_id: str. The id of the story.
            story_model: StoryModel. The story model to migrate.
            topic_id_to_topic: dict(str, Topic). The mapping from topic ID
                to topic.

        Returns:
            Result((str, Story), (str, Exception)). Result containing tuple that
            consists of story ID and either story object or Exception. Story
            object is returned when the migration was successful and Exception
            is returned otherwise.
        """
        try:
            story = story_fetchers.get_story_from_model(story_model)
            story.validate()
            assert topic_id_to_topic is not None
            corresponding_topic = (
                topic_id_to_topic[story.corresponding_topic_id])
            story_services.validate_prerequisite_skills_in_story_contents(
                corresponding_topic.get_all_skill_ids(), story.story_contents)
        except Exception as e:
            logging.exception(e)
            return result.Err((story_id, e))

        return result.Ok((story_id, story))
Exemplo n.º 6
0
    def map(item):
        if item.deleted:
            return

        story = story_fetchers.get_story_from_model(item)
        for node in story.story_contents.nodes:
            thumbnail_filename_and_size_value = '%s %s' % (
                node.thumbnail_filename, node.thumbnail_size_in_bytes)
            yield (item.id, thumbnail_filename_and_size_value)
Exemplo n.º 7
0
def delete_story(committer_id, story_id, force_deletion=False):
    """Deletes the story with the given story_id.

    Args:
        committer_id: str. ID of the committer.
        story_id: str. ID of the story to be deleted.
        force_deletion: bool. If true, the story and its history are fully
            deleted and are unrecoverable. Otherwise, the story and all
            its history are marked as deleted, but the corresponding models are
            still retained in the datastore. This last option is the preferred
            one.
    """

    story_model = story_models.StoryModel.get(story_id, strict=False)
    if story_model is not None:
        story = story_fetchers.get_story_from_model(story_model)
        exp_ids = story.story_contents.get_all_linked_exp_ids()
        story_model.delete(
            committer_id,
            feconf.COMMIT_MESSAGE_STORY_DELETED,
            force_deletion=force_deletion
        )
        # Reject the suggestions related to the exploration used in
        # the story.
        suggestion_services.auto_reject_translation_suggestions_for_exp_ids(
            exp_ids)

    exploration_context_models = (
        exp_models.ExplorationContextModel.get_all().filter(
            exp_models.ExplorationContextModel.story_id == story_id
        ).fetch()
    )
    exp_models.ExplorationContextModel.delete_multi(
        exploration_context_models
    )

    # This must come after the story is retrieved. Otherwise the memcache
    # key will be reinstated.
    caching_services.delete_multi(
        caching_services.CACHE_NAMESPACE_STORY, None, [story_id])

    # Delete the summary of the story (regardless of whether
    # force_deletion is True or not).
    delete_story_summary(story_id)

    # Delete the opportunities available.
    opportunity_services.delete_exp_opportunities_corresponding_to_story(
        story_id)
Exemplo n.º 8
0
    def _validate_chapter_title(
            cls, item, field_name_to_external_model_references):
        """Validate that chapter_title matches the title of the corresponding
        node of StoryModel.

        Args:
            item: datastore_services.Model. ExplorationOpportunitySummaryModel
                to validate.
            field_name_to_external_model_references:
                dict(str, (list(base_model_validators.ExternalModelReference))).
                A dict keyed by field name. The field name represents
                a unique identifier provided by the storage
                model to which the external model is associated. Each value
                contains a list of ExternalModelReference objects corresponding
                to the field_name. For examples, all the external Exploration
                Models corresponding to a storage model can be associated
                with the field name 'exp_ids'. This dict is used for
                validation of External Model properties linked to the
                storage model.
        """
        story_model_references = (
            field_name_to_external_model_references['story_ids'])

        for story_model_reference in story_model_references:
            story_model = story_model_reference.model_instance
            if story_model is None or story_model.deleted:
                model_class = story_model_reference.model_class
                model_id = story_model_reference.model_id
                cls._add_error(
                    'story_ids %s' % (
                        base_model_validators.ERROR_CATEGORY_FIELD_CHECK),
                    'Entity id %s: based on field story_ids having'
                    ' value %s, expected model %s with id %s but it doesn\'t'
                    ' exist' % (
                        item.id, model_id, model_class.__name__, model_id))
                continue
            story = story_fetchers.get_story_from_model(story_model)
            corresponding_story_node = (
                story.story_contents.get_node_with_corresponding_exp_id(
                    item.id))

            if item.chapter_title != corresponding_story_node.title:
                cls._add_error(
                    'chapter title check',
                    'Entity id %s: Chapter title: %s does not match the '
                    'chapter title of external story model: %s' % (
                        item.id, item.chapter_title,
                        corresponding_story_node.title))
Exemplo n.º 9
0
    def test_migrate_story_contents_to_latest_schema(self):
        story_id = story_services.get_new_story_id()
        topic_id = topic_services.get_new_topic_id()
        user_id = 'user_id'
        self.save_new_topic(topic_id, user_id, 'Topic', 'abbrev', None,
                            'A new topic', [], [], [], [], 0)
        self.save_new_story(story_id, user_id, 'Title', 'Description', 'Notes',
                            topic_id)

        story_model = story_models.StoryModel.get(story_id)
        self.assertEqual(story_model.story_contents_schema_version, 1)

        swap_story_object = self.swap(story_domain, 'Story', MockStoryObject)
        current_schema_version_swap = self.swap(
            feconf, 'CURRENT_STORY_CONTENTS_SCHEMA_VERSION', 2)

        with swap_story_object, current_schema_version_swap:
            story = story_fetchers.get_story_from_model(story_model)

        self.assertEqual(story.story_contents_schema_version, 2)
Exemplo n.º 10
0
    def test_get_story_from_model(self):
        story_model = story_models.StoryModel.get(self.STORY_ID)
        story = story_fetchers.get_story_from_model(story_model)

        self.assertEqual(story.to_dict(), self.story.to_dict())
Exemplo n.º 11
0
 def _get_model_domain_object_instance(cls, item):
     return story_fetchers.get_story_from_model(item)
Exemplo n.º 12
0
 def test_get_story_from_model(self):
     schema_version = feconf.CURRENT_STORY_CONTENTS_SCHEMA_VERSION - 1
     story_model = story_models.StoryModel.get(self.STORY_ID)
     story_model.story_contents_schema_version = schema_version
     story = story_fetchers.get_story_from_model(story_model)
     self.assertEqual(story.to_dict(), self.story.to_dict())