diff --git a/fresnel_lens/configuration.py b/fresnel_lens/configuration.py index c8495a3..3089102 100644 --- a/fresnel_lens/configuration.py +++ b/fresnel_lens/configuration.py @@ -1,5 +1,3 @@ -import datetime -import enum import json import os from dataclasses import dataclass @@ -10,9 +8,7 @@ from typing import Dict from typing import NamedTuple from typing import Optional from typing import Sequence -from typing import Tuple - -import peewee +from typing import Union from fresnel_lens import constants @@ -42,83 +38,141 @@ def _default_sqlite_pragmas() -> Dict[str, Any]: @dataclass -class _DBSqliteConfig: - path: Path = Path.cwd() / "fresnel_lens.db" +class DatabaseSqliteConfig: + path: Path = Path.cwd() / "fresnel.db" pragmas: Dict[str, Any] = field(default_factory=_default_sqlite_pragmas) @classmethod - def build(cls): + def from_env(cls): return cls( - path=Path(os.environ.get(constants.ENV_CONF_DB_SQLITE_PATH, cls.path)), - pragmas=json.loads(os.environ[constants.ENV_CONF_DB_SQLITE_PRAGMAS]) - if constants.ENV_CONF_DB_SQLITE_PRAGMAS in os.environ - else _default_sqlite_pragmas(), + path=Path(os.environ.get("FRESNEL_DB_SQLITE_PATH", cls.path)), + pragmas=json.loads(os.environ["FRESNEL_DB_SQLITE_PRAGMAS"]) + if "FRESNEL_DB_SQLITE_PRAGMAS" in os.environ + else constants.DEFAULT_SQLITE_PRAGMAS, ) @dataclass -class _DBMariaConfig: +class DatabaseMariaConfig: hostname: str = "localhost" username: str = "root" password: Optional[str] = None port: int = 3306 - schema: str = "fresnel_lens" + schema: str = "fresnel" @classmethod - def build(cls): + def from_env(cls): return cls( - hostname=os.getenv(constants.ENV_CONF_DB_MARIA_HOSTNAME, cls.hostname), - username=os.getenv(constants.ENV_CONF_DB_MARIA_USERNAME, cls.username), - password=os.environ.get(constants.ENV_CONF_DB_MARIA_PASSWORD, cls.password), - port=int(os.environ.get(constants.ENV_CONF_DB_MARIA_PORT, cls.port)), - schema=os.getenv(constants.ENV_CONF_DB_MARIA_SCHEMA, cls.schema), + hostname=os.getenv("FRESNEL_DB_MARIA_HOSTNAME", cls.hostname), + username=os.getenv("FRESNEL_DB_MARIA_USERNAME", cls.username), + password=os.environ.get("FRESNEL_DB_MARIA_PASSWORD", cls.password), + port=int(os.environ.get("FRESNEL_DB_MARIA_PORT", cls.port)), + schema=os.getenv("FRESNEL_DB_MARIA_SCHEMA", cls.schema), ) @dataclass -class _DBConfig: +class DatabaseConfig: backend: constants.DatabaseBackend = constants.DatabaseBackend.SQLITE - sqlite: _DBSqliteConfig = field(default_factory=_DBSqliteConfig.build) - mariadb: _DBMariaConfig = field(default_factory=_DBMariaConfig.build) + sqlite: DatabaseSqliteConfig = field(default_factory=DatabaseSqliteConfig.from_env) + mariadb: DatabaseMariaConfig = field(default_factory=DatabaseMariaConfig.from_env) @classmethod - def build(cls): + def from_env(cls): return cls( - backend=constants.DatabaseBackend[os.environ[constants.ENV_CONF_DB_BACKEND]] - if constants.ENV_CONF_DB_BACKEND in os.environ + backend=constants.DatabaseBackend[os.environ["FRESNEL_DB_BACKEND"].upper()] + if "FRESNEL_DB_BACKEND" in os.environ else cls.backend ) @dataclass class ManipConfig: - alias: str + name: str + strategy: constants.DimensionStrategy = constants.DimensionStrategy.SCALE + anchor: constants.Anchor = constants.Anchor.C formats: Sequence[constants.ImageFormat] = ( constants.ImageFormat.JPEG, constants.ImageFormat.PNG, ) - horizontal: None - vertical: None - - -@dataclass -class ImageMuckConfig: - database: _DBConfig = field(default_factory=_DBConfig.build) - images: Path = Path.cwd() / "images" - cache_dir: Path = Path.cwd() / "cache" - expose_source: bool = False - manips: Sequence[ManipConfig] = () + horizontal: Optional[Union[int, float]] = None + vertical: Optional[Union[int, float]] = None @classmethod - def from_env(cls): + def from_env(cls, key: str): + strategy = ( + constants.DimensionStrategy[ + os.environ[f"FRESNEL_MANIP_{key}_STRATEGY"].upper() + ] + if f"FRESNEL_MANIP_{key}_STRATEGY" in os.environ + else cls.strategy + ) + + dimension_conversion = ( + float if strategy == constants.DimensionStrategy.RELATIVE else int + ) + return cls( - storage_path=Path( - os.getenv(constants.ENV_CONF_FS_STORAGE_PATH, cls.storage_path) - ).resolve() + name=os.getenv(f"FRESNEL_MANIP_{key}_NAME", key.lower()), + strategy=strategy, + anchor=constants.Anchor(os.environ[f"FRESNEL_MANIP_{key}_ANCHOR"].lower()) + if f"FRESNEL_MANIP_{key}_ANCHOR" in os.environ + else cls.anchor, + formats=[ + constants.ImageFormat[item.upper()] + for item in os.environ[f"FRESNEL_MANIP_{key}_FORMATS"].split(",") + ] + if f"FRESNEL_MANIP_{key}_FORMATS" in os.environ + else cls.formats, + horizontal=dimension_conversion( + os.environ[f"FRESNEL_MANIP_{key}_HORIZONTAL"] + ) + if f"FRESNEL_MANIP_{key}_HORIZONTAL" in os.environ + else cls.horizontal, + vertical=dimension_conversion(os.environ[f"FRESNEL_MANIP_{key}_VERTICAL"]) + if f"FRESNEL_MANIP_{key}_VERTICAL" in os.environ + else cls.vertical, ) -def load() -> ImageMuckConfig: - return ImageMuckConfig.from_env() +@dataclass +class FresnelConfig: + database: DatabaseConfig = field(default_factory=DatabaseConfig.from_env) + sourcedir: Path = Path.cwd() / "images" + manipdir: Path = Path.cwd() / "images" + expose_source: bool = False + private: bool = False + manips: Dict[str, ManipConfig] = field(default_factory=dict) + + @classmethod + def from_env(cls): + manip_names = set( + [ + key.replace("FRESNEL_MANIP_", "").partition("_")[0] + for key in os.environ.keys() + if key.startswith("FRESNEL_MANIP_") + ] + ) + return cls( + sourcedir=Path(os.environ.get("FRESNEL_SOURCEDIR", cls.sourcedir)) + .expanduser() + .resolve(), + manipdir=Path(os.environ.get("FRESNEL_MANIPDIR", cls.manipdir)) + .expanduser() + .resolve(), + expose_source=os.getenv( + "FRESNEL_EXPOSE_SOURCE", str(cls.expose_source) + ).lower() + == "true", + private=os.getenv("FRESNEL_PRIVATE", str(cls.private)).lower() == "true", + manips={name.lower(): ManipConfig.from_env(name) for name in manip_names}, + ) + + +def load() -> FresnelConfig: + try: + return FresnelConfig.from_env() + except (ValueError, TypeError, IndexError, KeyError) as err: + raise RuntimeError(err) diff --git a/fresnel_lens/constants.py b/fresnel_lens/constants.py index 34651d8..978e321 100644 --- a/fresnel_lens/constants.py +++ b/fresnel_lens/constants.py @@ -15,15 +15,27 @@ class DimensionStrategy(enum.Enum): class ImageFormat(enum.Enum): - JPEG = ("jpg", "jpeg") - PNG = ("png",) - GIF = ("gif",) + JPEG = enum.auto() + PNG = enum.auto() + GIF = enum.auto() class Anchor(enum.Enum): + TL = "top-left" + TC = "top-center" + TR = "top-center" + CL = "center-left" C = "center" + CR = "center-right" + BL = "bottom-left" + BC = "bottom-center" + BR = "bottom-right" -HTTP_HEADER_RESPONSE_VERSION = "x-fresnel_lens-version" - -HTTP_HEADER_RESPONSE_DIGEST = "Digest" +DEFAULT_SQLITE_PRAGMAS = { + "journal_mode": "wal", + "cache_size": -1 * 64000, + "foreign_keys": 1, + "ignore_check_constraints": 0, + "synchronous": 0, +}