1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-02-09 09:57:36 +03:00

Updated model saving to use "setattr" instead of "__dict__.update". Allow use of properties, etc...

This commit is contained in:
Adolfo Gómez García 2023-11-09 17:10:07 +01:00
parent 3451719675
commit a23ca0eb73
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
4 changed files with 67 additions and 56 deletions

View File

@ -73,14 +73,16 @@ class Images(ModelHandler):
] ]
def beforeSave(self, fields: typing.Dict[str, typing.Any]) -> None: def beforeSave(self, fields: typing.Dict[str, typing.Any]) -> None:
fields['data'] = Image.prepareForDb(Image.decode64(fields['data']))[2] fields['image'] = fields['data']
del fields['data']
#fields['data'] = Image.prepareForDb(Image.decode64(fields['data']))[2]
def afterSave(self, item: 'Model') -> None: def afterSave(self, item: 'Model') -> None:
item = ensure.is_instance(item, Image) item = ensure.is_instance(item, Image)
# Updates the thumbnail and re-saves it # Updates the thumbnail and re-saves it
logger.debug('After save: item = %s', item) logger.debug('After save: item = %s', item)
item.updateThumbnail() #item.updateThumbnail()
item.save() #item.save()
def getGui(self, type_: str) -> typing.List[typing.Any]: def getGui(self, type_: str) -> typing.List[typing.Any]:
return self.addField( return self.addField(

View File

@ -1115,7 +1115,9 @@ class ModelHandler(BaseModelHandler):
for v in self.remove_fields: for v in self.remove_fields:
if v in args: if v in args:
del args[v] del args[v]
item.__dict__.update(args) # Update fields from args for k, v in args.items():
setattr(item, k, v)
# item.__dict__.update(args) # Update fields from args
# Now if tags, update them # Now if tags, update them
if isinstance(item, TaggingMixin): if isinstance(item, TaggingMixin):
@ -1156,7 +1158,7 @@ class ModelHandler(BaseModelHandler):
raise exceptions.NotFound('Item not found') from None raise exceptions.NotFound('Item not found') from None
except IntegrityError: # Duplicate key probably except IntegrityError: # Duplicate key probably
raise exceptions.RequestError('Element already exists (duplicate key error)') from None raise exceptions.RequestError('Element already exists (duplicate key error)') from None
except (exceptions.SaveException, udsexceptions.validation.ValidationError) as e: except (exceptions.SaveException, udsExceptions.validation.ValidationError) as e:
raise exceptions.RequestError(str(e)) from e raise exceptions.RequestError(str(e)) from e
except (exceptions.RequestError, exceptions.ResponseError): except (exceptions.RequestError, exceptions.ResponseError):
raise raise

View File

@ -325,7 +325,7 @@ class Command(BaseCommand):
gallery[galleryItem.name] = { gallery[galleryItem.name] = {
'size': f'{galleryItem.width}x{galleryItem.height}', 'size': f'{galleryItem.width}x{galleryItem.height}',
'stamp': galleryItem.stamp, 'stamp': galleryItem.stamp,
'length': len(galleryItem.data), 'length': galleryItem.length,
} }
tree[counter('GALLERY')] = gallery tree[counter('GALLERY')] = gallery

View File

@ -28,9 +28,11 @@
""" """
Author: Adolfo Gómez, dkmaster at dkmon dot com Author: Adolfo Gómez, dkmaster at dkmon dot com
""" """
from email.mime import base
import io import io
import codecs import base64
import logging import logging
from operator import ne
import typing import typing
@ -41,8 +43,8 @@ from django.http import HttpResponse
from .uuid_model import UUIDModel from .uuid_model import UUIDModel
from ..core.util.model import getSqlDatetime from uds.core.util.model import getSqlDatetime
from uds.core import consts
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -86,17 +88,7 @@ class Image(UUIDModel):
app_label = 'uds' app_label = 'uds'
@staticmethod @staticmethod
def encode64(data: bytes) -> str: def resizeAndConvert(image: PIL.Image.Image, size: typing.Tuple[int, int]) -> typing.Tuple[int, int, bytes]:
return codecs.encode(data, 'base64').decode().replace('\n', '')
@staticmethod
def decode64(data64: str) -> bytes:
return codecs.decode(data64.encode(), 'base64')
@staticmethod
def resizeAndConvert(
image: PIL.Image.Image, size: typing.Tuple[int, int]
) -> typing.Tuple[int, int, bytes]:
""" """
Resizes an image to the given size Resizes an image to the given size
""" """
@ -110,10 +102,8 @@ class Image(UUIDModel):
try: try:
stream = io.BytesIO(data) stream = io.BytesIO(data)
image = PIL.Image.open(stream) image = PIL.Image.open(stream)
except ( except Exception: # Image data is incorrect, replace as a simple transparent image
Exception image = PIL.Image.new('RGBA', Image.MAX_IMAGE_SIZE)
): # Image data is incorrect, replace as a simple transparent image
image = PIL.Image.new('RGBA', (128, 128))
# Max image size, keeping aspect and using antialias # Max image size, keeping aspect and using antialias
return Image.resizeAndConvert(image, Image.MAX_IMAGE_SIZE) return Image.resizeAndConvert(image, Image.MAX_IMAGE_SIZE)
@ -123,28 +113,14 @@ class Image(UUIDModel):
""" """
Returns the value of the image (data) as a base 64 encoded string Returns the value of the image (data) as a base 64 encoded string
""" """
return Image.encode64(self.data) return base64.b64encode(typing.cast(bytes, self.data)).decode()
@data64.setter
def data64(self, value: str) -> None:
"""
Sets the value of image (data) from a base 64 encoded string
"""
self.data = Image.decode64(value)
@property @property
def thumb64(self) -> str: def thumb64(self) -> str:
""" """
Returns the value of the image (data) as a base 64 encoded string Returns the value of the image (data) as a base 64 encoded string
""" """
return Image.encode64(self.thumb) return base64.b64encode(self.thumb).decode()
@thumb64.setter
def thumb64(self, value: str) -> None:
"""
Sets the value of image (data) from a base 64 encoded string
"""
self.thumb = Image.decode64(value)
@property @property
def image(self) -> PIL.Image.Image: def image(self) -> PIL.Image.Image:
@ -157,6 +133,49 @@ class Image(UUIDModel):
except Exception: # Image data is incorrect, fix as a simple transparent image except Exception: # Image data is incorrect, fix as a simple transparent image
return PIL.Image.new('RGBA', Image.MAX_IMAGE_SIZE) return PIL.Image.new('RGBA', Image.MAX_IMAGE_SIZE)
@image.setter
def image(self, value: typing.Union[bytes, str, PIL.Image.Image]) -> None:
"""Set image from bytes, base64 string or PIL Image
Bytes: raw data
String: base64 encoded data
Image: PIL Image
Args:
value (typing.Union[bytes, str, Image.Image]): Image data
Raises:
ValueError: Invalid image type
Note:
This method also creates the thumbnail
Not saved to database until save() is called
"""
data: bytes = b''
if value:
if isinstance(value, bytes):
data = value
elif isinstance(value, str):
data = base64.b64decode(value)
elif isinstance(value, PIL.Image.Image):
with io.BytesIO() as output:
value.save(output, format='PNG')
data = output.getvalue()
else:
raise ValueError('Invalid image type')
self.width, self.height, self.data = Image.prepareForDb(data)
# Setup thumbnail
with io.BytesIO(data) as input:
with PIL.Image.open(input) as img:
img.thumbnail(Image.THUMBNAIL_SIZE, PIL.Image.LANCZOS)
with io.BytesIO() as output:
img.save(output, format='PNG')
self.thumb = output.getvalue()
else:
self.data = consts.images.DEFAULT_IMAGE
self.thumb = consts.images.DEFAULT_THUMB
@property @property
def size(self) -> typing.Tuple[int, int]: def size(self) -> typing.Tuple[int, int]:
""" """
@ -164,24 +183,12 @@ class Image(UUIDModel):
""" """
return self.width, self.height return self.width, self.height
def updateThumbnail(self) -> None: @property
img = self.image def length(self) -> int:
_, _, self.thumb = Image.resizeAndConvert(img, Image.THUMBNAIL_SIZE)
def _processImageStore(self) -> None:
self.width, self.height, self.data = Image.prepareForDb(self.data)
self.updateThumbnail()
def storeImageFromBinary(self, data: bytes) -> None:
self.data = data
self._processImageStore()
def storeImageFromBase64(self, data64: str):
""" """
Stores an image, passed as base64 string, resizing it as necessary Returns the image size
""" """
self.data64 = data64 return len(self.data)
self._processImageStore()
def imageResponse(self) -> HttpResponse: def imageResponse(self) -> HttpResponse:
return HttpResponse(self.data, content_type='image/png') return HttpResponse(self.data, content_type='image/png')