Loading resources/models.py +5 −0 Original line number Diff line number Diff line Loading @@ -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) Loading rpc/const.py +4 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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" rpc/methods.py +73 −3 Original line number Diff line number Diff line Loading @@ -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__) Loading Loading @@ -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) Loading Loading @@ -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 = {} Loading Loading @@ -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) Loading
resources/models.py +5 −0 Original line number Diff line number Diff line Loading @@ -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) Loading
rpc/const.py +4 −0 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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"
rpc/methods.py +73 −3 Original line number Diff line number Diff line Loading @@ -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__) Loading Loading @@ -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) Loading Loading @@ -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 = {} Loading Loading @@ -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)