mirror of
https://github.com/enpaul/kodak.git
synced 2024-12-27 18:23:31 +00:00
Update database schema for new image/alias structure
This commit is contained in:
parent
1b7856c590
commit
158db1209b
@ -4,14 +4,16 @@ from typing import Tuple
|
||||
import peewee
|
||||
|
||||
from kodak import constants
|
||||
from kodak import exceptions
|
||||
from kodak.configuration import KodakConfig
|
||||
from kodak.database._shared import INTERFACE as interface
|
||||
from kodak.database._shared import KodakModel
|
||||
from kodak.database.access import AccessRecord
|
||||
from kodak.database.alias import AliasRecord
|
||||
from kodak.database.image import ImageRecord
|
||||
from kodak.database.thumbnail import ThumbnailRecord
|
||||
|
||||
|
||||
MODELS: Tuple[KodakModel, ...] = (ImageRecord, ThumbnailRecord)
|
||||
MODELS: Tuple[KodakModel, ...] = (ImageRecord, AliasRecord, AccessRecord)
|
||||
|
||||
|
||||
def initialize(config: KodakConfig):
|
||||
@ -26,13 +28,13 @@ def initialize(config: KodakConfig):
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if config.database.backend == constants.SupportedDatabaseBackend.SQLITE:
|
||||
if config.database.backend == constants.DatabaseBackend.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:
|
||||
elif config.database.backend == constants.DatabaseBackend.MARIADB:
|
||||
logger.debug("Using MariaDB database backend")
|
||||
logger.debug(
|
||||
"Configuring MariaDB:"
|
||||
@ -48,7 +50,7 @@ def initialize(config: KodakConfig):
|
||||
charset="utf8mb4",
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
raise exceptions.ConfigurationError(
|
||||
f"Invalid storage backend in configuration: {config.database.backend}"
|
||||
)
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
import datetime
|
||||
import enum
|
||||
import hashlib
|
||||
import uuid
|
||||
from typing import NamedTuple
|
||||
from typing import Type
|
||||
|
||||
import peewee
|
||||
|
||||
@ -7,7 +11,82 @@ import peewee
|
||||
INTERFACE = peewee.DatabaseProxy()
|
||||
|
||||
|
||||
class Checksum(NamedTuple):
|
||||
"""Checksum data container
|
||||
|
||||
:param algorithm: Hashing algorithm, must be supported by Python's hashlib
|
||||
:param digest: Hex digest of the hash
|
||||
"""
|
||||
|
||||
algorithm: str
|
||||
digest: str
|
||||
|
||||
@classmethod
|
||||
def from_hash(cls, data: hashlib._hashlib.HASH): # pylint: disable=protected-access
|
||||
"""Construct from a hashlib object"""
|
||||
return cls(algorithm=data.name, digest=data.hexdigest())
|
||||
|
||||
|
||||
class EnumField(peewee.CharField):
|
||||
"""Custom field for storing enums"""
|
||||
|
||||
def __init__(self, enumeration: Type[enum.Enum], *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.enumeration = enumeration
|
||||
|
||||
def db_value(self, value: enum.Enum) -> str:
|
||||
"""Convert the enum value to the database string
|
||||
|
||||
:param value: Enum item to store the name of
|
||||
:raises peewee.IntegrityError: When the item passed to ``value`` is not in the field's enum
|
||||
:returns: The name of the enum item passed to ``value``
|
||||
"""
|
||||
if not isinstance(value, self.enumeration):
|
||||
raise peewee.IntegrityError(
|
||||
f"Enum {self.enumeration.__name__} has no value '{value}'"
|
||||
)
|
||||
return value.name
|
||||
|
||||
def python_value(self, value: str) -> enum.Enum:
|
||||
"""Convert the stored string to the corresponding enum
|
||||
|
||||
:param value: Name of the item from the field's enum to return
|
||||
:raises peewee.IntegrityError: When the name passed to ``value`` does not correspond to an
|
||||
item in the field's enum
|
||||
:returns: The enum item with the name passed to ``value``
|
||||
"""
|
||||
try:
|
||||
return self.enumeration[value]
|
||||
except KeyError:
|
||||
raise peewee.InterfaceError(
|
||||
f"Enum {self.enumeration.__name__} has no value with name '{value}'"
|
||||
) from None
|
||||
|
||||
|
||||
class ChecksumField(peewee.CharField):
|
||||
"""Field for storing checksum hashes in the database
|
||||
|
||||
.. note:: The reason for implementing this is to protect against future changes to the hashing
|
||||
algorithm. Just storing the digest means that if the hashing algorithm is ever
|
||||
changed (for performance, etc) then any existing records will be invalidated. By
|
||||
storing the hashing algorithm with the digest we can protect against that possibility.
|
||||
A custom container needs to be implemented because the builtin hashlib has no way to
|
||||
recreate a hash object from the algorithm+digest without the original data.
|
||||
"""
|
||||
|
||||
def db_value(self, value: Checksum) -> str:
|
||||
"""Serialize the checkstum to a database string"""
|
||||
return f"{value.algorithm}:{value.digest}"
|
||||
|
||||
def python_value(self, value: str) -> Checksum:
|
||||
"""Deserailize a string to a checksum container"""
|
||||
alg, _, digest = value.partition(":")
|
||||
return Checksum(algorithm=alg, digest=digest)
|
||||
|
||||
|
||||
class KodakModel(peewee.Model):
|
||||
"""Base model for defining common fields and attaching database"""
|
||||
|
||||
class Meta: # pylint: disable=too-few-public-methods,missing-class-docstring
|
||||
database = INTERFACE
|
||||
|
||||
|
9
kodak/database/access.py
Normal file
9
kodak/database/access.py
Normal file
@ -0,0 +1,9 @@
|
||||
import peewee
|
||||
|
||||
from kodak.database._shared import KodakModel
|
||||
|
||||
|
||||
class AccessRecord(KodakModel):
|
||||
"""Model for access keys when operating in private mode"""
|
||||
|
||||
password = peewee.CharField(null=False)
|
13
kodak/database/alias.py
Normal file
13
kodak/database/alias.py
Normal file
@ -0,0 +1,13 @@
|
||||
import peewee
|
||||
|
||||
from kodak.database._shared import ChecksumField
|
||||
from kodak.database._shared import KodakModel
|
||||
from kodak.database.image import ImageRecord
|
||||
|
||||
|
||||
class AliasRecord(KodakModel):
|
||||
"""Model for manipulated image records"""
|
||||
|
||||
parent = peewee.ForeignKeyField(ImageRecord, null=False)
|
||||
name = peewee.CharField(null=False)
|
||||
checksum = ChecksumField(null=False)
|
@ -1,30 +1,15 @@
|
||||
import json
|
||||
import uuid
|
||||
from typing import List
|
||||
|
||||
import peewee
|
||||
|
||||
from kodak import constants
|
||||
from kodak.database._shared import ChecksumField
|
||||
from kodak.database._shared import EnumField
|
||||
from kodak.database._shared import KodakModel
|
||||
|
||||
|
||||
class ImageRecord(KodakModel):
|
||||
"""Database record for"""
|
||||
"""Model for source images"""
|
||||
|
||||
width = peewee.IntegerField(null=False)
|
||||
height = peewee.IntegerField(null=False)
|
||||
format = peewee.CharField(null=False)
|
||||
name = peewee.Charfield(null=False)
|
||||
format = EnumField(constants.ImageFormat, 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])
|
||||
checksum = ChecksumField(null=False)
|
||||
|
@ -1,11 +0,0 @@
|
||||
import peewee
|
||||
|
||||
from kodak.database._shared import KodakModel
|
||||
from kodak.database.image import ImageRecord
|
||||
|
||||
|
||||
class ThumbnailRecord(KodakModel):
|
||||
|
||||
parent = peewee.ForeignKeyField(ImageRecord)
|
||||
width = peewee.IntegerField(null=False)
|
||||
height = peewee.IntegerField(null=False)
|
Loading…
Reference in New Issue
Block a user