refactor: move image upload to a unified function

This commit is contained in:
s3lph 2024-12-11 00:51:55 +01:00
parent 59c9bb2e71
commit 54086dea39
Signed by: s3lph
GPG key ID: 0AA29A52FB33CFB5
6 changed files with 67 additions and 126 deletions

View file

@ -0,0 +1,31 @@
import os
from io import BytesIO
from shutil import copyfile
import magic
from PIL import Image
def upload_thumbnail(image_data: bytes, filename: str, size: int = 150) -> bool:
# Only process the image, if its size is more than zero. Zero size means no new image was uploaded
if image_data is None or len(image_data) == 0:
return False
# Detect the MIME type
filemagic: magic.FileMagic = magic.detect_from_content(image_data)
if not filemagic.mime_type.startswith('image/'):
raise ValueError(f'Unsupported file type: {filemagic.mime_type}')
# Create the absolute path of the upload directory
dirname: str = os.path.abspath(os.path.dirname(filename))
os.makedirs(dirname, exist_ok=True)
# Parse the image data
try:
image: Image = Image.open(BytesIO(image_data))
except IOError:
raise ValueError(f'Unsupported file type: {filemagic.mime_type}')
# Resize the image to 150x150
image.thumbnail((150, 150), Image.LANCZOS)
# Write the image to the file
image.save(filename, 'PNG')
return True

View file

@ -1,16 +1,13 @@
import os
from datetime import datetime, UTC
from io import BytesIO
from shutil import copyfile
import magic
from PIL import Image
from bottle import get, post, abort, redirect, request, FormsDict
from matemat.db import MatematDatabase
from matemat.db.primitives import User, ReceiptPreference
from matemat.exceptions import AuthenticationError, DatabaseConsistencyError
from matemat.util.currency_format import parse_chf
from matemat.util.thumbnails import upload_thumbnail
from matemat.webserver import session, template
from matemat.webserver.config import get_app_config, get_stock_provider
from matemat.webserver.template import Notification
@ -101,22 +98,11 @@ def handle_change(args: FormsDict, files: FormsDict, db: MatematDatabase):
newproduct = db.create_product(name, price_member, price_non_member, custom_price, stockable, barcode)
# If a new product image was uploaded, process it
image = files.image.file.read() if 'image' in files else None
filename = os.path.join(os.path.abspath(config['UploadDir']), f'thumbnails/products/{newproduct.id}.png')
if image is not None and len(image) > 0:
# Detect the MIME type
filemagic: magic.FileMagic = magic.detect_from_content(image)
if not filemagic.mime_type.startswith('image/'):
return
# Create the absolute path of the upload directory
abspath: str = os.path.join(os.path.abspath(config['UploadDir']), 'thumbnails/products/')
os.makedirs(abspath, exist_ok=True)
try:
# Parse the image data
image: Image = Image.open(BytesIO(image))
# Resize the image to 150x150
image.thumbnail((150, 150), Image.LANCZOS)
# Write the image to the file
image.save(os.path.join(abspath, f'{newproduct.id}.png'), 'PNG')
except OSError as e:
upload_thumbnail(image, filename)
except Exception as e:
Notification.error(str(e), decay=True)
return
@ -145,24 +131,11 @@ def handle_change(args: FormsDict, files: FormsDict, db: MatematDatabase):
continue
# Read the raw image data from the request
default: bytes = files[category].file.read()
# Only process the image, if its size is more than zero. Zero size means no new image was uploaded
if len(default) == 0:
continue
# Detect the MIME type
filemagic: magic.FileMagic = magic.detect_from_content(default)
if not filemagic.mime_type.startswith('image/'):
continue
# Create the absolute path of the upload directory
abspath: str = os.path.join(os.path.abspath(config['UploadDir']), f'thumbnails/{category}/')
os.makedirs(abspath, exist_ok=True)
filename: str = os.path.join(os.path.abspath(config['UploadDir']), f'thumbnails/{category}/default.png')
try:
# Parse the image data
image: Image = Image.open(BytesIO(default))
# Resize the image to 150x150
image.thumbnail((150, 150), Image.LANCZOS)
# Write the image to the file
image.save(os.path.join(abspath, f'default.png'), 'PNG')
except OSError as e:
if upload_thumbnail(default, filename):
Notification.success(f'{category} default image updated successfully.', decay=True)
except Exception as e:
Notification.error(str(e), decay=True)
return

View file

@ -1,18 +1,17 @@
import os
from io import BytesIO
from datetime import datetime, UTC
from typing import Dict
import magic
from PIL import Image
from bottle import get, post, redirect, abort, request, FormsDict
from matemat.db import MatematDatabase
from matemat.db.primitives import Product
from matemat.exceptions import DatabaseConsistencyError
from matemat.util.currency_format import parse_chf
from matemat.util.thumbnails import upload_thumbnail
from matemat.webserver import template, session
from matemat.webserver.config import get_app_config, get_stock_provider
from matemat.webserver.template import Notification
@get('/modproduct')
@ -136,23 +135,9 @@ def handle_change(args: FormsDict, files: FormsDict, product: Product, db: Matem
# If a new product image was uploaded, process it
if 'image' in files:
# Read the raw image data from the request
avatar = files.image.file.read()
# Only process the image, if its size is more than zero. Zero size means no new image was uploaded
if len(avatar) == 0:
return
# Detect the MIME type
filemagic: magic.FileMagic = magic.detect_from_content(avatar)
if not filemagic.mime_type.startswith('image/'):
return
# Create the absolute path of the upload directory
abspath: str = os.path.join(os.path.abspath(config['UploadDir']), 'thumbnails/products/')
os.makedirs(abspath, exist_ok=True)
image = files.image.file.read()
filename: str = os.path.join(os.path.abspath(config['UploadDir']), f'thumbnails/products/{product.id}.png')
try:
# Parse the image data
image: Image = Image.open(BytesIO(avatar))
# Resize the image to 150x150
image.thumbnail((150, 150), Image.LANCZOS)
# Write the image to the file
image.save(os.path.join(abspath, f'{product.id}.png'), 'PNG')
except OSError:
return
upload_thumbnail(image, filename)
except Exception as e:
Notification.error(str(e), decay=True)

View file

@ -1,18 +1,17 @@
import os
from datetime import datetime, UTC
from io import BytesIO
from typing import Dict, Optional
import magic
from PIL import Image
from bottle import get, post, redirect, abort, request, FormsDict
from matemat.db import MatematDatabase
from matemat.db.primitives import User, ReceiptPreference
from matemat.exceptions import DatabaseConsistencyError
from matemat.util.currency_format import parse_chf
from matemat.util.thumbnails import upload_thumbnail
from matemat.webserver import template, session
from matemat.webserver.config import get_app_config
from matemat.webserver.template import Notification
@get('/moduser')
@ -144,23 +143,9 @@ def handle_change(args: FormsDict, files: FormsDict, user: User, authuser: User,
# If a new avatar was uploaded, process it
if 'avatar' in files:
# Read the raw image data from the request
avatar = files.avatar.file.read()
# Only process the image, if its size is more than zero. Zero size means no new image was uploaded
if len(avatar) == 0:
return
# Detect the MIME type
filemagic: magic.FileMagic = magic.detect_from_content(avatar)
if not filemagic.mime_type.startswith('image/'):
return
# Create the absolute path of the upload directory
abspath: str = os.path.join(os.path.abspath(config['UploadDir']), 'thumbnails/users/')
os.makedirs(abspath, exist_ok=True)
image = files.avatar.file.read()
filename: str = os.path.join(os.path.abspath(config['UploadDir']), f'thumbnails/users/{user.id}.png')
try:
# Parse the image data
image: Image = Image.open(BytesIO(avatar))
# Resize the image to 150x150
image.thumbnail((150, 150), Image.LANCZOS)
# Write the image to the file
image.save(os.path.join(abspath, f'{user.id}.png'), 'PNG')
except OSError:
return
upload_thumbnail(image, filename)
except Exception as e:
Notification.error(str(e), decay=True)

View file

@ -1,16 +1,13 @@
import os
from datetime import datetime, UTC
from io import BytesIO
from shutil import copyfile
import magic
from PIL import Image
from bottle import get, post, abort, redirect, request, FormsDict
from matemat.db import MatematDatabase
from matemat.db.primitives import User, ReceiptPreference
from matemat.exceptions import AuthenticationError, DatabaseConsistencyError
from matemat.util.currency_format import parse_chf
from matemat.util.thumbnails import upload_thumbnail
from matemat.webserver import session, template
from matemat.webserver.config import get_app_config, get_stock_provider
from matemat.webserver.template import Notification
@ -149,31 +146,15 @@ def handle_change(args: FormsDict, files: FormsDict, user: User, db: MatematData
Notification.success(f'Token {token.name} removed', decay=True)
# The user requested an avatar change
elif change == 'avatar':
# The new avatar field must be present
if 'avatar' not in files:
return
elif change == 'avatar' and 'avatar' in files:
# Read the raw image data from the request
avatar = files.avatar.file.read()
# Only process the image, if its size is more than zero. Zero size means no new image was uploaded
if len(avatar) == 0:
return
# Detect the MIME type
filemagic: magic.FileMagic = magic.detect_from_content(avatar)
if not filemagic.mime_type.startswith('image/'):
return
# Create the absolute path of the upload directory
abspath: str = os.path.join(os.path.abspath(config['UploadDir']), 'thumbnails/users/')
os.makedirs(abspath, exist_ok=True)
image = files.avatar.file.read()
filename: str = os.path.join(os.path.abspath(config['UploadDir']), f'thumbnails/users/{user.id}.png')
try:
# Parse the image data
image: Image = Image.open(BytesIO(avatar))
# Resize the image to 150x150
image.thumbnail((150, 150), Image.LANCZOS)
# Write the image to the file
image.save(os.path.join(abspath, f'{user.id}.png'), 'PNG')
except OSError:
return
if upload_thumbnail(image, filename):
Notification.success('Avatar changed')
except Exception as e:
Notification.error(str(e), decay=True)
except UnicodeDecodeError:
raise ValueError('an argument not a string')

View file

@ -1,15 +1,12 @@
import os
from shutil import copyfile
from io import BytesIO
import magic
from bottle import get, post, redirect, abort, request, FormsDict
from PIL import Image
import netaddr
from matemat.db import MatematDatabase
from matemat.db.primitives import User
from matemat.util.thumbnails import upload_thumbnail
from matemat.webserver import template, session
from matemat.webserver.config import get_app_config
@ -33,21 +30,9 @@ def signup_user(args: FormsDict, files: FormsDict, db: MatematDatabase) -> User:
touchkey = str(args.touchkey)
db.change_touchkey(new_user, password, touchkey, verify_password=False)
# Finally, set the avatar, if provided
if 'avatar' in files:
avatar = files.avatar.file.read()
else:
avatar = None
if avatar:
filemagic: magic.FileMagic = magic.detect_from_content(avatar)
if filemagic.mime_type.startswith('image/'):
abspath: str = os.path.join(os.path.abspath(config['UploadDir']), 'thumbnails/users/')
os.makedirs(abspath, exist_ok=True)
# Parse the image data
image: Image = Image.open(BytesIO(avatar))
# Resize the image to 150x150
image.thumbnail((150, 150), Image.LANCZOS)
# Write the image to the file
image.save(os.path.join(abspath, f'{new_user.id}.png'), 'PNG')
image = files.avatar.file.read() if 'avatar' in files else None
filename: str = os.path.join(os.path.abspath(config['UploadDir']), f'thumbnails/users/{new_user.id}.png')
upload_thumbnail(image, filename)
return new_user
@ -73,7 +58,8 @@ def signup():
with MatematDatabase(config['DatabaseFile']) as db:
try:
user = signup_user(request.params, request.files, db)
except ValueError as e:
except Exception as e:
Notification.error(str(e), decay=True)
redirect('/signup')
# Set the user ID session variable
session.put(session_id, 'authenticated_user', user.id)