Commit c876574f authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

Add rotate and crop functionality

parent a3254b66
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -380,6 +380,11 @@ class File(Resource):
        slugged_title = slugify(self.title)
        return slugged_title + extension

    @property
    def original_name_extension(self) -> str:
        _, ext = os.path.splitext(self.original_name)
        return ext


class Collection(models.Model):
    title = models.CharField("titre", max_length=256)
+4 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ NO_SUCH_USER = "no such user"
NO_PROJECT_ROOT = "project has no root"
NO_SUCH_PROJECT_PROPERTY = "project has no such property"
NOT_A_FILE = "not a file"
NOT_AN_IMAGE = "not an image"
PERM_COLLECTION_CREATE = "collection.create"
PERM_COLLECTION_DELETE = "collection.delete"
PERM_COLLECTION_PUBLIC_ONLY = "collection.public_only"
@@ -45,5 +46,8 @@ SUPERUSER_NEEDED = "only for superusers"
TOO_MANY_SEARCH_TERMS = "Too many search terms."
WRONG_ARGUMENT = "wrong argument"
NO_FOOTGUNS = "Can't shoot your own foot"
NOTHING_TO_DO = "nothing to do"
CANT_CREATE_IMAGE = "Can't create image"
PROJECT_MISMATCH = "Can't mix items from different collections"
INVALID_PROPERTY_KEY = "invalid property key"
UNKNOWN_ERROR = "unknown error"
+73 −3
Original line number Diff line number Diff line
@@ -30,7 +30,10 @@ from inspect import signature as _signature
from functools import wraps as _wraps
from rpc.const import *
import logging
import sys
import pyvips
import tempfile
from resources.helpers import handle_local_file


logger = logging.getLogger(__name__)

@@ -2210,7 +2213,7 @@ def replace_file(user: User, from_resource_id: int, to_resource_id: int) -> bool
        # delete from_resource
        from_resource_instance.delete()
        # save to_resource
        to_resource_instance.save()
        to_resource_instance.file.save()
        return True
    except Resource.DoesNotExist:
        raise ServiceException(NO_SUCH_RESOURCE)
@@ -2637,7 +2640,6 @@ def meta_count(user: User, metadata_id: int, collection_id: int) -> dict:
    meta = Metadata.objects.filter(pk=metadata_id).first()
    if not meta:
        raise ServiceException(NO_SUCH_METADATA)
    print(meta.title)
    collections_ids = collection_instance.descendants_and_self_ids()

    return_dict = {}
@@ -2754,3 +2756,71 @@ def project_properties(user: User, project_id: int) -> List[dict]:
    for prop in ProjectProperty.objects.filter(project_id=project_id):
        data.append(serializers.project_property(prop))
    return data


@_log_call
@_rpc_groups(["Resources"])
def picture_rotate_crop(
    user: User,
    resource_id: int,
    rotation: float = 0.0,
    top_crop: int = 0,
    right_crop: int = 0,
    bottom_crop: int = 0,
    left_crop: int = 0,
) -> dict:
    """
    Rotate and crop an image. The resulting image then replaces the
    original in the current resource.

    Will return the resource upon success. Throws a ServiceException
    otherwise.
    """
    file_instance = File.objects.filter(pk=resource_id, deleted_at__isnull=True).first()
    if not file_instance:
        raise ServiceException(NO_SUCH_RESOURCE)
    _check_project_permission(user, file_instance.project, PERM_RESOURCE_UPDATE)
    if not file_instance.file:
        raise ServiceException(NOT_A_FILE)
    if not file_instance.file.should_have_iiif():
        raise ServiceException(NOT_AN_IMAGE)
    try:
        top_crop = int(top_crop)
        right_crop = int(right_crop)
        bottom_crop = int(bottom_crop)
        left_crop = int(left_crop)
        rotation = float(rotation)
        if rotation > 360 or rotation < 0:
            raise ValueError
    except ValueError:
        raise ServiceException(WRONG_ARGUMENT)
    if rotation + top_crop + right_crop + bottom_crop + left_crop == 0:
        raise ServiceException(NOTHING_TO_DO)
    tmp_pic_path = "{}/resize-{}-{}-{}-{}-{}-{}{}".format(
        tempfile.gettempdir(),
        file_instance.pk,
        rotation,
        top_crop,
        right_crop,
        bottom_crop,
        left_crop,
        file_instance.original_name_extension,
    )
    try:
        img = pyvips.Image.new_from_file(file_instance.local_path())
        if rotation:
            img = img.rotate(rotation)
        img = img.extract_area(
            left_crop,
            top_crop,
            img.width - left_crop - right_crop,
            img.height - top_crop - bottom_crop,
        )
        img.write_to_file(tmp_pic_path)
        new_file_id = handle_local_file(tmp_pic_path, file_instance.project)
        if not replace_file(user, new_file_id, resource_id):
            raise ServiceException(UNKNOWN_ERROR)
        reloaded_file = File.objects.get(id=resource_id)
        return serializers.file(reloaded_file, include_metas=False)
    except pyvips.error.Error:
        raise ServiceException(CANT_CREATE_IMAGE)