From e265dda12dff58205db4bb070aa38a1f7396db17 Mon Sep 17 00:00:00 2001 From: Ethan Paul <24588726+enpaul@users.noreply.github.com> Date: Mon, 21 Sep 2020 23:10:18 -0400 Subject: [PATCH] Add initial database schema and infrastructure components --- imagemonk/database/__init__.py | 58 +++++++++++++++++++++++++++++++++ imagemonk/database/_shared.py | 15 +++++++++ imagemonk/database/image.py | 30 +++++++++++++++++ imagemonk/database/thumbnail.py | 11 +++++++ 4 files changed, 114 insertions(+) create mode 100644 imagemonk/database/__init__.py create mode 100644 imagemonk/database/_shared.py create mode 100644 imagemonk/database/image.py create mode 100644 imagemonk/database/thumbnail.py diff --git a/imagemonk/database/__init__.py b/imagemonk/database/__init__.py new file mode 100644 index 0000000..96f8bf2 --- /dev/null +++ b/imagemonk/database/__init__.py @@ -0,0 +1,58 @@ +import logging +from typing import Tuple + +import peewee + +from imagemonk import constants +from imagemonk.configuration import ImageMonkConfig +from imagemonk.database._shared import ImageMonkModel +from imagemonk.database._shared import INTERFACE as interface +from imagemonk.database.image import ImageRecord +from imagemonk.database.thumbnail import ThumbnailRecord + + +MODELS: Tuple[ImageMonkModel, ...] = (ImageRecord, ThumbnailRecord) + + +def initialize(config: ImageMonkConfig): + """Initialize the database interface + + Defining the database as an + `unconfigured proxy object `_ + allows it to be configured at runtime based on the config values. + + :param config: Populated configuration container object + """ + + logger = logging.getLogger(__name__) + + if config.database.backend == constants.SupportedDatabaseBackend.SQLITE: + logger.debug("Using SQLite database backend") + logger.debug(f"Applying SQLite pragmas: {config.database.sqlite.pragmas}") + database = peewee.SqliteDatabase( + config.database.sqlite.path, pragmas=config.database.sqlite.pragmas + ) + elif config.database.backend == constants.SupportedDatabaseBackend.MARIADB: + logger.debug("Using MariaDB database backend") + logger.debug( + "Configuring MariaDB:" + f" {config.database.mariadb.username}@{config.database.mariadb.hostname}:{config.database.mariadb.port}," + f" with database '{config.database.mariadb.schema}'" + ) + database = peewee.MySQLDatabase( + config.database.mariadb.schema, + host=config.database.mariadb.hostname, + port=config.database.mariadb.port, + user=config.database.mariadb.username, + password=config.database.mariadb.password, + charset="utf8mb4", + ) + else: + raise ValueError( + f"Invalid storage backend in configuration: {config.database.backend}" + ) + + interface.initialize(database) + + with interface.atomic(): + interface.create_tables(MODELS) diff --git a/imagemonk/database/_shared.py b/imagemonk/database/_shared.py new file mode 100644 index 0000000..285afb8 --- /dev/null +++ b/imagemonk/database/_shared.py @@ -0,0 +1,15 @@ +import datetime +import uuid + +import peewee + + +INTERFACE = peewee.DatabaseProxy() + + +class ImageMonkModel(peewee.Model): + class Meta: # pylint: disable=too-few-public-methods,missing-class-docstring + database = INTERFACE + + uuid = peewee.UUIDField(null=False, unique=True, default=uuid.uuid4) + created = peewee.DateTimeField(null=False, default=datetime.datetime.utcnow) diff --git a/imagemonk/database/image.py b/imagemonk/database/image.py new file mode 100644 index 0000000..9e66860 --- /dev/null +++ b/imagemonk/database/image.py @@ -0,0 +1,30 @@ +import json +import uuid +from typing import List + +import peewee + +from imagemonk.database._shared import ImageMonkModel + + +class ImageRecord(ImageMonkModel): + """Database record for""" + + width = peewee.IntegerField(null=False) + height = peewee.IntegerField(null=False) + format = peewee.CharField(null=False) + deleted = peewee.BooleanField(null=False, default=False) + public = peewee.BooleanField(null=False, default=False) + owner = peewee.UUIDField(null=False) + sha256 = peewee.CharField(null=False) + _readable = peewee.CharField(null=False, default="[]") + + @property + def readable(self) -> List[uuid.UUID]: + """List of UUIDs corresponding to accounts that can read the file""" + return [uuid.UUID(item) for item in json.loads(self._readable)] + + @readable.setter + def readable(self, value: List[uuid.UUID]): + """Update the list of UUIDs for accounts that can read the file""" + self._readable = json.dumps([str(item) for item in value]) diff --git a/imagemonk/database/thumbnail.py b/imagemonk/database/thumbnail.py new file mode 100644 index 0000000..760b871 --- /dev/null +++ b/imagemonk/database/thumbnail.py @@ -0,0 +1,11 @@ +import peewee + +from imagemonk.database._shared import ImageMonkModel +from imagemonk.database.image import ImageRecord + + +class ThumbnailRecord(ImageMonkModel): + + parent = peewee.ForeignKeyField(ImageRecord) + width = peewee.IntegerField(null=False) + height = peewee.IntegerField(null=False)