mirror of
https://github.com/enpaul/kodak.git
synced 2024-11-23 15:07:13 +00:00
Reimplement manip configuration settings
Add support for future color changes Once and for all settle how crop/scaling will work Add proper exceptions
This commit is contained in:
parent
67abbd5374
commit
dff79571ba
@ -1,3 +1,4 @@
|
|||||||
|
import enum
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@ -5,18 +6,40 @@ from dataclasses import field
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import NamedTuple
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Sequence
|
from typing import Set
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from kodak import constants
|
from kodak import constants
|
||||||
|
from kodak import exceptions
|
||||||
|
|
||||||
|
|
||||||
class DimensionConfig(NamedTuple):
|
def _get_int(var: str, default: Optional[int]) -> Optional[int]:
|
||||||
strategy: constants.DimensionStrategy
|
return int(os.environ[var]) if var in os.environ else default
|
||||||
anchor: constants.Anchor
|
|
||||||
value: int
|
|
||||||
|
def _get_float(var: str, default: Optional[float]) -> Optional[float]:
|
||||||
|
return float(os.environ[var]) if var in os.environ else default
|
||||||
|
|
||||||
|
|
||||||
|
def _get_enum_by_name(
|
||||||
|
var: str, enumeration: enum.Enum, default: enum.Enum
|
||||||
|
) -> enum.Enum:
|
||||||
|
return enumeration[os.environ[var].upper()] if var in os.environ else default
|
||||||
|
|
||||||
|
|
||||||
|
def _get_enum_by_value(
|
||||||
|
var: str, enumeration: enum.Enum, default: enum.Enum
|
||||||
|
) -> enum.Enum:
|
||||||
|
return enumeration(os.environ[var].lower()) if var in os.environ else default
|
||||||
|
|
||||||
|
|
||||||
|
def _get_path(var: str, default: Union[str, Path]) -> Path:
|
||||||
|
return Path(os.environ.get(var, default)).expanduser().resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_bool(var: str, default: bool) -> bool:
|
||||||
|
return os.getenv(var, str(default)).lower() == "true"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -29,9 +52,9 @@ class DatabaseSqliteConfig:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_env(cls):
|
def from_env(cls):
|
||||||
return cls(
|
return cls(
|
||||||
path=Path(os.environ.get("KODAK_DB_SQLITE_PATH", cls.path)),
|
path=_get_path("KODAK_DATABASE_SQLITE_PATH", cls.path),
|
||||||
pragmas=json.loads(os.environ["KODAK_DB_SQLITE_PRAGMAS"])
|
pragmas=json.loads(os.environ["KODAK_DATABASE_SQLITE_PRAGMAS"])
|
||||||
if "KODAK_DB_SQLITE_PRAGMAS" in os.environ
|
if "KODAK_DATABASE_SQLITE_PRAGMAS" in os.environ
|
||||||
else constants.DEFAULT_SQLITE_PRAGMAS,
|
else constants.DEFAULT_SQLITE_PRAGMAS,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,16 +66,16 @@ class DatabaseMariaConfig:
|
|||||||
username: str = "root"
|
username: str = "root"
|
||||||
password: Optional[str] = None
|
password: Optional[str] = None
|
||||||
port: int = 3306
|
port: int = 3306
|
||||||
schema: str = "fresnel"
|
schema: str = "kodak"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_env(cls):
|
def from_env(cls):
|
||||||
return cls(
|
return cls(
|
||||||
hostname=os.getenv("KODAK_DB_MARIA_HOSTNAME", cls.hostname),
|
hostname=os.getenv("KODAK_DATABASE_MARIADB_HOSTNAME", cls.hostname),
|
||||||
username=os.getenv("KODAK_DB_MARIA_USERNAME", cls.username),
|
username=os.getenv("KODAK_DATABASE_MARIADB_USERNAME", cls.username),
|
||||||
password=os.environ.get("KODAK_DB_MARIA_PASSWORD", cls.password),
|
password=os.environ.get("KODAK_DATABASE_MARIADB_PASSWORD", cls.password),
|
||||||
port=int(os.environ.get("KODAK_DB_MARIA_PORT", cls.port)),
|
port=_get_int("KODAK_DATABASE_MARIADB_PORT", cls.port),
|
||||||
schema=os.getenv("KODAK_DB_MARIA_SCHEMA", cls.schema),
|
schema=os.getenv("KODAK_DATABASE_MARIADB_SCHEMA", cls.schema),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -66,56 +89,86 @@ class DatabaseConfig:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_env(cls):
|
def from_env(cls):
|
||||||
return cls(
|
return cls(
|
||||||
backend=constants.DatabaseBackend[os.environ["KODAK_DB_BACKEND"].upper()]
|
backend=_get_enum_by_name(
|
||||||
if "KODAK_DB_BACKEND" in os.environ
|
"KODAK_DATABASE_BACKEND", constants.DatabaseBackend, cls.backend
|
||||||
else cls.backend
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ManipCropConfig:
|
||||||
|
horizontal: Optional[int] = None
|
||||||
|
vertical: Optional[int] = None
|
||||||
|
anchor: constants.CropAnchor = constants.CropAnchor.C
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_env(cls, key: str):
|
||||||
|
return cls(
|
||||||
|
anchor=_get_enum_by_value(
|
||||||
|
f"KODAK_MANIP_{key}_CROP_ANCHOR", constants.CropAnchor, cls.anchor
|
||||||
|
),
|
||||||
|
horizontal=_get_int(f"KODAK_MANIP_{key}_CROP_HORIZONTAL", cls.horizontal),
|
||||||
|
vertical=_get_int(f"KODAK_MANIP_{key}_CROP_VERTICAL", cls.vertical),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ManipScaleConfig:
|
||||||
|
horizontal: Optional[Union[int, float]] = None
|
||||||
|
vertical: Optional[Union[int, float]] = None
|
||||||
|
strategy: constants.ScaleStrategy = constants.ScaleStrategy.ABSOLUTE
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_env(cls, key: str):
|
||||||
|
strategy = _get_enum_by_name(
|
||||||
|
f"KODAK_MANIP_{key}_SCALE_STRATEGY", constants.ScaleStrategy, cls.strategy
|
||||||
|
)
|
||||||
|
|
||||||
|
if strategy == constants.ScaleStrategy.ABSOLUTE:
|
||||||
|
parser = _get_int
|
||||||
|
elif strategy == constants.ScaleStrategy.RELATIVE:
|
||||||
|
parser = _get_float
|
||||||
|
else:
|
||||||
|
raise RuntimeError("This path should not be possible")
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
strategy=strategy,
|
||||||
|
vertical=parser(f"KODAK_MANIP_{key}_SCALE_VERTICAL", cls.vertical),
|
||||||
|
horizontal=parser(f"KODAK_MANIP_{key}_SCALE_HORIZONTAL", cls.horizontal),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ManipConfig:
|
class ManipConfig:
|
||||||
name: str
|
name: str
|
||||||
strategy: constants.DimensionStrategy = constants.DimensionStrategy.SCALE
|
crop: ManipCropConfig = field(default_factory=ManipCropConfig.from_env)
|
||||||
anchor: constants.Anchor = constants.Anchor.C
|
scale: ManipScaleConfig = field(default_factory=ManipScaleConfig.from_env)
|
||||||
formats: Sequence[constants.ImageFormat] = (
|
formats: Set[constants.ImageFormat] = field(
|
||||||
constants.ImageFormat.JPEG,
|
default_factory=lambda: constants.DEFAULT_SUPPORTED_FORMATS
|
||||||
constants.ImageFormat.PNG,
|
|
||||||
)
|
)
|
||||||
horizontal: Optional[Union[int, float]] = None
|
black_and_white: bool = False
|
||||||
vertical: Optional[Union[int, float]] = None
|
# TODO: Implement support for these settings
|
||||||
|
# brightness: int = 0
|
||||||
|
# contrast: int = 0
|
||||||
|
# sepia: bool = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_env(cls, key: str):
|
def from_env(cls, key: str):
|
||||||
strategy = (
|
|
||||||
constants.DimensionStrategy[
|
|
||||||
os.environ[f"KODAK_MANIP_{key}_STRATEGY"].upper()
|
|
||||||
]
|
|
||||||
if f"KODAK_MANIP_{key}_STRATEGY" in os.environ
|
|
||||||
else cls.strategy
|
|
||||||
)
|
|
||||||
|
|
||||||
dimension_conversion = (
|
|
||||||
float if strategy == constants.DimensionStrategy.RELATIVE else int
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
name=os.getenv(f"KODAK_MANIP_{key}_NAME", key.lower()),
|
name=os.getenv(f"KODAK_MANIP_{key}_NAME", key.lower()),
|
||||||
strategy=strategy,
|
crop=ManipCropConfig.from_env(key),
|
||||||
anchor=constants.Anchor(os.environ[f"KODAK_MANIP_{key}_ANCHOR"].lower())
|
scale=ManipScaleConfig.from_env(key),
|
||||||
if f"KODAK_MANIP_{key}_ANCHOR" in os.environ
|
formats=set(
|
||||||
else cls.anchor,
|
[
|
||||||
formats=[
|
constants.ImageFormat[item.strip().upper()]
|
||||||
constants.ImageFormat[item.upper()]
|
|
||||||
for item in os.environ[f"KODAK_MANIP_{key}_FORMATS"].split(",")
|
for item in os.environ[f"KODAK_MANIP_{key}_FORMATS"].split(",")
|
||||||
]
|
]
|
||||||
|
)
|
||||||
if f"KODAK_MANIP_{key}_FORMATS" in os.environ
|
if f"KODAK_MANIP_{key}_FORMATS" in os.environ
|
||||||
else cls.formats,
|
else constants.DEFAULT_SUPPORTED_FORMATS,
|
||||||
horizontal=dimension_conversion(os.environ[f"KODAK_MANIP_{key}_HORIZONTAL"])
|
black_and_white=_get_bool(
|
||||||
if f"KODAK_MANIP_{key}_HORIZONTAL" in os.environ
|
f"KODAK_MANIP_{key}_BLACK_AND_WHITE", cls.black_and_white
|
||||||
else cls.horizontal,
|
),
|
||||||
vertical=dimension_conversion(os.environ[f"KODAK_MANIP_{key}_VERTICAL"])
|
|
||||||
if f"KODAK_MANIP_{key}_VERTICAL" in os.environ
|
|
||||||
else cls.vertical,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -138,17 +191,10 @@ class KodakConfig:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
return cls(
|
return cls(
|
||||||
sourcedir=Path(os.environ.get("KODAK_SOURCEDIR", cls.sourcedir))
|
sourcedir=_get_path("KODAK_SOURCEDIR", cls.sourcedir),
|
||||||
.expanduser()
|
manipdir=_get_path("KODAK_MANIPDIR", cls.manipdir),
|
||||||
.resolve(),
|
expose_source=_get_bool("KODAK_EXPOSE_SOURCE", cls.expose_source),
|
||||||
manipdir=Path(os.environ.get("KODAK_MANIPDIR", cls.manipdir))
|
private=_get_bool("KODAK_PRIVATE", cls.private),
|
||||||
.expanduser()
|
|
||||||
.resolve(),
|
|
||||||
expose_source=os.getenv(
|
|
||||||
"KODAK_EXPOSE_SOURCE", str(cls.expose_source)
|
|
||||||
).lower()
|
|
||||||
== "true",
|
|
||||||
private=os.getenv("KODAK_PRIVATE", str(cls.private)).lower() == "true",
|
|
||||||
manips={name.lower(): ManipConfig.from_env(name) for name in manip_names},
|
manips={name.lower(): ManipConfig.from_env(name) for name in manip_names},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -157,4 +203,4 @@ def load() -> KodakConfig:
|
|||||||
try:
|
try:
|
||||||
return KodakConfig.from_env()
|
return KodakConfig.from_env()
|
||||||
except (ValueError, TypeError, IndexError, KeyError) as err:
|
except (ValueError, TypeError, IndexError, KeyError) as err:
|
||||||
raise RuntimeError(err)
|
raise exceptions.ConfigurationError(f"Failed to load configuration: {err}")
|
||||||
|
@ -8,19 +8,17 @@ class DatabaseBackend(enum.Enum):
|
|||||||
SQLITE = peewee.SqliteDatabase
|
SQLITE = peewee.SqliteDatabase
|
||||||
|
|
||||||
|
|
||||||
class DimensionStrategy(enum.Enum):
|
class ScaleStrategy(enum.Enum):
|
||||||
CROP = enum.auto()
|
|
||||||
SCALE = enum.auto()
|
|
||||||
RELATIVE = enum.auto()
|
RELATIVE = enum.auto()
|
||||||
|
ABSOLUTE = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
class ImageFormat(enum.Enum):
|
class ImageFormat(enum.Enum):
|
||||||
JPEG = enum.auto()
|
JPEG = enum.auto()
|
||||||
PNG = enum.auto()
|
PNG = enum.auto()
|
||||||
GIF = enum.auto()
|
|
||||||
|
|
||||||
|
|
||||||
class Anchor(enum.Enum):
|
class CropAnchor(enum.Enum):
|
||||||
TL = "top-left"
|
TL = "top-left"
|
||||||
TC = "top-center"
|
TC = "top-center"
|
||||||
TR = "top-center"
|
TR = "top-center"
|
||||||
@ -39,3 +37,5 @@ DEFAULT_SQLITE_PRAGMAS = {
|
|||||||
"ignore_check_constraints": 0,
|
"ignore_check_constraints": 0,
|
||||||
"synchronous": 0,
|
"synchronous": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFAULT_SUPPORTED_FORMATS = {ImageFormat.JPEG, ImageFormat.PNG}
|
||||||
|
@ -37,3 +37,7 @@ class ServerError(KodakException):
|
|||||||
|
|
||||||
class ImageFileRemovedError(ServerError):
|
class ImageFileRemovedError(ServerError):
|
||||||
"""Image file removed from server"""
|
"""Image file removed from server"""
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurationError(ServerError):
|
||||||
|
"""Failed to load the application configuration"""
|
||||||
|
Loading…
Reference in New Issue
Block a user