예제 #1
0
파일: suggestion.py 프로젝트: oppia/oppia
def _upload_suggestion_images(files, suggestion, filenames):
    """Saves a suggestion's images to storage.

    Args:
        files: dict. Files containing a mapping of image
            filename to image blob.
        suggestion: BaseSuggestion. The suggestion for which images are being
            uploaded.
        filenames: list(str). The image filenames.
    """
    suggestion_image_context = suggestion.image_context
    # TODO(#10513): Find a way to save the images before the suggestion is
    # created.
    for filename in filenames:
        image = files.get(filename)
        image = base64.decodebytes(image.encode('utf-8'))
        file_format = (image_validation_services.validate_image_and_filename(
            image, filename))
        image_is_compressible = (file_format
                                 in feconf.COMPRESSIBLE_IMAGE_FORMATS)
        fs_services.save_original_and_compressed_versions_of_image(
            filename, suggestion_image_context, suggestion.target_id, image,
            'image', image_is_compressible)

    target_entity_html_list = suggestion.get_target_entity_html_strings()
    target_image_filenames = (
        html_cleaner.get_image_filenames_from_html_strings(
            target_entity_html_list))

    fs_services.copy_images(suggestion.target_type, suggestion.target_id,
                            suggestion_image_context, suggestion.target_id,
                            target_image_filenames)
예제 #2
0
 def test_copy_images(self):
     with utils.open_file(os.path.join(feconf.TESTS_DATA_DIR, 'img.png'),
                          'rb',
                          encoding=None) as f:
         original_image_content = f.read()
     fs_services.save_original_and_compressed_versions_of_image(
         self.FILENAME, 'exploration', self.EXPLORATION_ID,
         original_image_content, 'image', True)
     destination_fs = fs_domain.AbstractFileSystem(
         fs_domain.GcsFileSystem(feconf.ENTITY_TYPE_QUESTION,
                                 'question_id1'))
     self.assertFalse(destination_fs.isfile('image/%s' % self.FILENAME))
     self.assertFalse(
         destination_fs.isfile('image/%s' % self.COMPRESSED_IMAGE_FILENAME))
     self.assertFalse(
         destination_fs.isfile('image/%s' % self.MICRO_IMAGE_FILENAME))
     fs_services.copy_images(feconf.ENTITY_TYPE_EXPLORATION,
                             self.EXPLORATION_ID,
                             feconf.ENTITY_TYPE_QUESTION, 'question_id1',
                             ['image.png'])
     self.assertTrue(destination_fs.isfile('image/%s' % self.FILENAME))
     self.assertTrue(
         destination_fs.isfile('image/%s' % self.COMPRESSED_IMAGE_FILENAME))
     self.assertTrue(
         destination_fs.isfile('image/%s' % self.MICRO_IMAGE_FILENAME))
예제 #3
0
    def map(item):
        if item.deleted:
            yield (FixQuestionImagesStorageOneOffJob._DELETED_KEY, 1)
            return

        question = question_fetchers.get_question_from_model(item)
        html_list = question.question_state_data.get_all_html_content_strings()
        image_filenames = html_cleaner.get_image_filenames_from_html_strings(
            html_list)
        file_system_class = fs_services.get_entity_file_system_class()
        question_fs = fs_domain.AbstractFileSystem(file_system_class(
            feconf.ENTITY_TYPE_QUESTION, question.id))
        success_count = 0
        # For each image filename, check if it exists in the correct path. If
        # not, copy the image file to the correct path else continue.
        for image_filename in image_filenames:
            if not question_fs.isfile('image/%s' % image_filename):
                for skill_id in question.linked_skill_ids:
                    skill_fs = fs_domain.AbstractFileSystem(file_system_class(
                        feconf.ENTITY_TYPE_SKILL, skill_id))
                    if skill_fs.isfile('image/%s' % image_filename):
                        fs_services.copy_images(
                            feconf.ENTITY_TYPE_SKILL, skill_id,
                            feconf.ENTITY_TYPE_QUESTION, question.id,
                            [image_filename])
                        success_count += 1
                        break
        if success_count > 0:
            yield (
                FixQuestionImagesStorageOneOffJob._IMAGE_COPIED,
                '%s image paths were fixed for question id %s with '
                'linked_skill_ids: %r' % (
                    success_count, question.id, question.linked_skill_ids))
예제 #4
0
    def accept(self, unused_commit_message):
        """Accepts the suggestion.

        Args:
            unused_commit_message: str. This parameter is passed in for
                consistency with the existing suggestions. As a default commit
                message is used in the add_question function, the arg is unused.
        """
        question_dict = self.change.question_dict
        question_dict['version'] = 1
        question_dict['id'] = (question_services.get_new_question_id())
        html_list = self.get_all_html_content_strings()
        filenames = (
            html_cleaner.get_image_filenames_from_html_strings(html_list))
        image_context = fs_services.get_image_context_for_suggestion_target(
            self.target_type)
        fs_services.copy_images(image_context, self.target_id,
                                feconf.ENTITY_TYPE_QUESTION, self.target_id,
                                filenames)

        question_dict['linked_skill_ids'] = [self.change.skill_id]
        question = question_domain.Question.from_dict(question_dict)
        question.validate()
        question_services.add_question(self.author_id, question)
        skill = skill_fetchers.get_skill_by_id(self.change.skill_id,
                                               strict=False)
        if skill is None:
            raise utils.ValidationError(
                'The skill with the given id doesn\'t exist.')
        question_services.create_new_question_skill_link(
            self.author_id, question_dict['id'], self.change.skill_id,
            self._get_skill_difficulty())
예제 #5
0
 def _copy_new_images_to_target_entity_storage(self):
     """Copy newly added images in suggestion to the target entity
     storage.
     """
     new_image_filenames = self.get_new_image_filenames_added_in_suggestion()
     fs_services.copy_images(
         self.image_context, self.target_id, self.target_type,
         self.target_id, new_image_filenames)
예제 #6
0
    def post(self):
        try:
            # The create_suggestion method needs to be run in transaction as it
            # generates multiple connected models (suggestion, feedback thread,
            # feedback message etc.) and all these models needs to be created
            # together, in a batch.
            suggestion = transaction_services.run_in_transaction(
                suggestion_services.create_suggestion,
                self.payload.get('suggestion_type'),
                self.payload.get('target_type'), self.payload.get('target_id'),
                self.payload.get('target_version_at_submission'), self.user_id,
                self.payload.get('change'), self.payload.get('description'))
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        # TODO(#10513) : Find a way to save the images before the suggestion is
        # created.
        suggestion_image_context = suggestion.image_context
        # For suggestion which doesn't need images for rendering the
        # image_context is set to None.
        if suggestion_image_context is None:
            self.render_json(self.values)
            return

        new_image_filenames = (
            suggestion.get_new_image_filenames_added_in_suggestion())
        for filename in new_image_filenames:
            image = self.request.get(filename)
            if not image:
                logging.error(
                    'Image not provided for file with name %s when the '
                    ' suggestion with target id %s was created.' %
                    (filename, suggestion.target_id))
                raise self.InvalidInputException(
                    'No image data provided for file with name %s.' %
                    (filename))
            try:
                file_format = (
                    image_validation_services.validate_image_and_filename(
                        image, filename))
            except utils.ValidationError as e:
                raise self.InvalidInputException('%s' % (e))
            image_is_compressible = (file_format
                                     in feconf.COMPRESSIBLE_IMAGE_FORMATS)
            fs_services.save_original_and_compressed_versions_of_image(
                filename, suggestion_image_context, suggestion.target_id,
                image, 'image', image_is_compressible)

        target_entity_html_list = suggestion.get_target_entity_html_strings()
        target_image_filenames = (
            html_cleaner.get_image_filenames_from_html_strings(
                target_entity_html_list))

        fs_services.copy_images(suggestion.target_type, suggestion.target_id,
                                suggestion_image_context, suggestion.target_id,
                                target_image_filenames)

        self.render_json(self.values)
예제 #7
0
    def post(self):
        """Handles POST requests."""
        if (self.payload.get('suggestion_type') ==
                feconf.SUGGESTION_TYPE_EDIT_STATE_CONTENT):
            raise self.InvalidInputException(
                'Content suggestion submissions are no longer supported.')

        try:
            suggestion = suggestion_services.create_suggestion(
                self.payload.get('suggestion_type'),
                self.payload.get('target_type'), self.payload.get('target_id'),
                self.payload.get('target_version_at_submission'),
                self.user_id, self.payload.get('change'),
                self.payload.get('description'))
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        # TODO(#10513) : Find a way to save the images before the suggestion is
        # created.
        suggestion_image_context = suggestion.image_context

        new_image_filenames = (
            suggestion.get_new_image_filenames_added_in_suggestion())
        for filename in new_image_filenames:
            image = self.request.get(filename)
            if not image:
                logging.exception(
                    'Image not provided for file with name %s when the '
                    ' suggestion with target id %s was created.' % (
                        filename, suggestion.target_id))
                raise self.InvalidInputException(
                    'No image data provided for file with name %s.'
                    % (filename))
            try:
                file_format = (
                    image_validation_services.validate_image_and_filename(
                        image, filename))
            except utils.ValidationError as e:
                raise self.InvalidInputException('%s' % (e))
            image_is_compressible = (
                file_format in feconf.COMPRESSIBLE_IMAGE_FORMATS)
            fs_services.save_original_and_compressed_versions_of_image(
                filename, suggestion_image_context, suggestion.target_id,
                image, 'image', image_is_compressible)

        target_entity_html_list = suggestion.get_target_entity_html_strings()
        target_image_filenames = (
            html_cleaner.get_image_filenames_from_html_strings(
                target_entity_html_list))

        fs_services.copy_images(
            suggestion.target_type, suggestion.target_id,
            suggestion_image_context, suggestion.target_id,
            target_image_filenames)

        self.render_json(self.values)
예제 #8
0
파일: suggestion.py 프로젝트: oppia/oppia
    def put(self, target_id, suggestion_id):
        """Handles PUT requests.

        Args:
            target_id: str. The ID of the suggestion target.
            suggestion_id: str. The ID of the suggestion.
        """
        if suggestion_id.split('.')[0] != feconf.ENTITY_TYPE_SKILL:
            raise self.InvalidInputException(
                'This handler allows actions only on suggestions to skills.')

        if suggestion_id.split('.')[1] != target_id:
            raise self.InvalidInputException(
                'The skill id provided does not match the skill id present as '
                'part of the suggestion_id')

        action = self.payload.get('action')

        if action == constants.ACTION_ACCEPT_SUGGESTION:
            # Question suggestions do not use commit messages.
            suggestion_services.accept_suggestion(
                suggestion_id, self.user_id, 'UNUSED_COMMIT_MESSAGE',
                self.payload.get('review_message'))

            suggestion = suggestion_services.get_suggestion_by_id(
                suggestion_id)
            target_entity_html_list = (
                suggestion.get_target_entity_html_strings())
            target_image_filenames = (
                html_cleaner.get_image_filenames_from_html_strings(
                    target_entity_html_list))

            fs_services.copy_images(suggestion.target_type,
                                    suggestion.target_id,
                                    feconf.IMAGE_CONTEXT_QUESTION_SUGGESTIONS,
                                    suggestion.target_id,
                                    target_image_filenames)
        elif action == constants.ACTION_REJECT_SUGGESTION:
            suggestion_services.reject_suggestion(
                suggestion_id, self.user_id,
                self.payload.get('review_message'))
        else:
            raise self.InvalidInputException('Invalid action.')

        self.render_json(self.values)
예제 #9
0
def _upload_suggestion_images(request, suggestion, filenames):
    """Saves a suggestion's images to storage.

    Args:
        request: webapp2.Request. Request object containing a mapping of image
            filename to image blob.
        suggestion: BaseSuggestion. The suggestion for which images are being
            uploaded.
        filenames: list(str). The image filenames.
    """
    suggestion_image_context = suggestion.image_context
    # TODO(#10513) : Find a way to save the images before the suggestion is
    # created.
    for filename in filenames:
        image = request.get(filename)
        if not image:
            logging.exception(
                'Image not provided for file with name %s when the '
                ' suggestion with target id %s was created.' %
                (filename, suggestion.target_id))
            raise base.BaseHandler.InvalidInputException(
                'No image data provided for file with name %s.' % (filename))
        try:
            file_format = (
                image_validation_services.validate_image_and_filename(
                    image, filename))
        except utils.ValidationError as e:
            raise base.BaseHandler.InvalidInputException('%s' % (e))
        image_is_compressible = (file_format
                                 in feconf.COMPRESSIBLE_IMAGE_FORMATS)
        fs_services.save_original_and_compressed_versions_of_image(
            filename, suggestion_image_context, suggestion.target_id, image,
            'image', image_is_compressible)

    target_entity_html_list = suggestion.get_target_entity_html_strings()
    target_image_filenames = (
        html_cleaner.get_image_filenames_from_html_strings(
            target_entity_html_list))

    fs_services.copy_images(suggestion.target_type, suggestion.target_id,
                            suggestion_image_context, suggestion.target_id,
                            target_image_filenames)