majuna/app/brm/utils.py

91 lines
3.1 KiB
Python

from __future__ import annotations
import base64
from io import BytesIO
from typing import Any, Tuple
import webcolors
from PIL import Image
from werkzeug.datastructures import FileStorage
def is_integer(contender: Any) -> bool:
"""
Determine if a string (or other object type that can be converted automatically) represents an integer.
Thanks to https://note.nkmk.me/en/python-check-int-float/.
:param contender: object to test
:return: true if it's an integer
"""
try:
float(contender)
except ValueError:
return False
else:
return float(contender).is_integer()
def thumbnail_uploaded_image(file: FileStorage, max_size: Tuple[int, int] = (256, 256)) -> bytes:
"""
Process an uploaded image file into a resized image of a specific size.
:param file: An uploaded image file.
:param max_size: A tuple containing the maximum width and height for the thumbnail image. Default is (256, 256).
:return: The byte data of the thumbnail image.
"""
if file.filename is None:
raise ValueError("No file was uploaded")
img = Image.open(file)
img.thumbnail(max_size)
byte_arr = BytesIO()
img.save(byte_arr, format='PNG' if file.filename.lower().endswith('.png') else 'JPEG')
return byte_arr.getvalue()
def create_data_uri(bytes_data: bytes, file_extension: str) -> str:
"""
Create a data URI from binary data and a file extension.
:param bytes_data: The binary data of an image.
:param file_extension: The file extension of the image.
:return: A data URI representing the image.
"""
# base64 encode
encoded = base64.b64encode(bytes_data).decode('ascii')
# create data URI
data_uri = "data:image/{};base64,{}".format('jpeg' if file_extension == 'jpg' else file_extension, encoded)
return data_uri
def normalize_color(color: str) -> str:
"""
Normalize a string representing a color to its hexadecimal representation.
This function accepts a string representing a color in one of the following formats:
- A CSS color name, such as 'red', 'green', or 'blue'.
- A 6-digit hexadecimal color code, such as '#FF0000' for red.
- A 3-digit hexadecimal color code, such as '#F00' for red.
The function returns a string with the color in 6-digit or 3-digit hexadecimal format,
with lowercase letters. If the color is given as a CSS color name, the function
returns its equivalent 6-digit hexadecimal format.
:param color: A string representing a color.
:return: The color in 6-digit or 3-digit hexadecimal format.
:raises: ValueError: If the input string does not represent a valid color in any of the accepted formats.
"""
try:
return webcolors.name_to_hex(color) # type: ignore[no-any-return]
except ValueError:
pass
if color.startswith('#'):
color = color[1:].lower()
if len(color) in [3, 6]:
try:
_ = int(color, 16)
return f"#{color}"
except ValueError:
pass
raise ValueError(f"color must be a valid HTML color, got: {repr(color)}")