2025-01-30 16:23:47 +01:00
|
|
|
import logging
|
2025-02-03 16:46:45 +01:00
|
|
|
from typing import List, overload, Self, Type, TypeVar
|
|
|
|
|
|
|
|
from flask import url_for
|
2025-01-30 16:23:47 +01:00
|
|
|
|
|
|
|
from .exceptions import NotFoundException
|
|
|
|
from .fields import BrickRecordFields
|
|
|
|
from .record_list import BrickRecordList
|
2025-01-31 16:34:52 +01:00
|
|
|
from .set_owner import BrickSetOwner
|
2025-01-30 16:23:47 +01:00
|
|
|
from .set_status import BrickSetStatus
|
2025-02-03 16:46:45 +01:00
|
|
|
from .set_storage import BrickSetStorage
|
2025-01-31 18:08:53 +01:00
|
|
|
from .set_tag import BrickSetTag
|
2025-01-30 16:23:47 +01:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2025-02-03 16:46:45 +01:00
|
|
|
T = TypeVar('T', BrickSetOwner, BrickSetStatus, BrickSetStorage, BrickSetTag)
|
2025-01-30 16:23:47 +01:00
|
|
|
|
|
|
|
|
|
|
|
# Lego sets metadata list
|
|
|
|
class BrickMetadataList(BrickRecordList[T]):
|
|
|
|
kind: str
|
|
|
|
mapping: dict[str, T]
|
|
|
|
model: Type[T]
|
|
|
|
|
|
|
|
# Database table
|
|
|
|
table: str
|
|
|
|
|
|
|
|
# Queries
|
|
|
|
select_query: str
|
|
|
|
|
2025-02-03 16:46:45 +01:00
|
|
|
# Set state endpoint
|
|
|
|
set_state_endpoint: str
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
model: Type[T],
|
|
|
|
/,
|
|
|
|
*,
|
|
|
|
force: bool = False,
|
|
|
|
records: list[T] | None = None
|
|
|
|
):
|
|
|
|
self.model = model
|
|
|
|
|
|
|
|
# Records override (masking the class variables with instance ones)
|
|
|
|
if records is not None:
|
2025-02-03 23:45:35 +01:00
|
|
|
self.override()
|
2025-02-03 16:46:45 +01:00
|
|
|
|
|
|
|
for metadata in records:
|
|
|
|
self.records.append(metadata)
|
|
|
|
self.mapping[metadata.fields.id] = metadata
|
|
|
|
else:
|
|
|
|
# Load metadata only if there is none already loaded
|
|
|
|
records = getattr(self, 'records', None)
|
|
|
|
|
|
|
|
if records is None or force:
|
|
|
|
# Don't use super()__init__ as it would mask class variables
|
|
|
|
self.fields = BrickRecordFields()
|
|
|
|
|
|
|
|
logger.info('Loading {kind} list'.format(
|
|
|
|
kind=self.kind
|
|
|
|
))
|
|
|
|
|
|
|
|
self.__class__.records = []
|
|
|
|
self.__class__.mapping = {}
|
|
|
|
|
|
|
|
# Load the metadata from the database
|
|
|
|
for record in self.select():
|
|
|
|
metadata = model(record=record)
|
|
|
|
|
|
|
|
self.__class__.records.append(metadata)
|
|
|
|
self.__class__.mapping[metadata.fields.id] = metadata
|
|
|
|
|
|
|
|
# HTML prefix name
|
|
|
|
def as_prefix(self, /) -> str:
|
|
|
|
return self.kind.replace(' ', '-')
|
2025-01-30 16:23:47 +01:00
|
|
|
|
2025-02-03 16:46:45 +01:00
|
|
|
# Filter the list of records (this one does nothing)
|
|
|
|
def filter(self) -> list[T]:
|
|
|
|
return self.records
|
2025-01-30 16:23:47 +01:00
|
|
|
|
2025-02-03 23:45:35 +01:00
|
|
|
# Add a layer of override data
|
|
|
|
def override(self) -> None:
|
|
|
|
self.fields = BrickRecordFields()
|
|
|
|
|
|
|
|
self.records = []
|
|
|
|
self.mapping = {}
|
|
|
|
|
2025-01-30 16:23:47 +01:00
|
|
|
# Return the items as columns for a select
|
2025-02-03 16:46:45 +01:00
|
|
|
@classmethod
|
|
|
|
def as_columns(cls, /, **kwargs) -> str:
|
|
|
|
new = cls.new()
|
|
|
|
|
2025-01-30 16:23:47 +01:00
|
|
|
return ', '.join([
|
|
|
|
'"{table}"."{column}"'.format(
|
2025-02-03 16:46:45 +01:00
|
|
|
table=cls.table,
|
2025-01-30 16:23:47 +01:00
|
|
|
column=record.as_column(),
|
|
|
|
)
|
|
|
|
for record
|
2025-02-03 16:46:45 +01:00
|
|
|
in new.filter(**kwargs)
|
2025-01-30 16:23:47 +01:00
|
|
|
])
|
|
|
|
|
|
|
|
# Grab a specific status
|
2025-02-03 16:46:45 +01:00
|
|
|
@classmethod
|
|
|
|
def get(cls, id: str, /, *, allow_none: bool = False) -> T:
|
|
|
|
new = cls.new()
|
|
|
|
|
|
|
|
if allow_none and id == '':
|
|
|
|
return new.model()
|
|
|
|
|
|
|
|
if id not in new.mapping:
|
2025-01-30 16:23:47 +01:00
|
|
|
raise NotFoundException(
|
|
|
|
'{kind} with ID {id} was not found in the database'.format(
|
2025-02-03 16:46:45 +01:00
|
|
|
kind=new.kind.capitalize(),
|
2025-01-30 16:23:47 +01:00
|
|
|
id=id,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2025-02-03 16:46:45 +01:00
|
|
|
return new.mapping[id]
|
2025-01-30 16:23:47 +01:00
|
|
|
|
|
|
|
# Get the list of statuses depending on the context
|
2025-02-03 16:46:45 +01:00
|
|
|
@overload
|
|
|
|
@classmethod
|
|
|
|
def list(cls, /, **kwargs) -> List[T]: ...
|
|
|
|
|
|
|
|
@overload
|
|
|
|
@classmethod
|
|
|
|
def list(cls, /, as_class: bool = False, **kwargs) -> Self: ...
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def list(cls, /, as_class: bool = False, **kwargs) -> List[T] | Self:
|
|
|
|
new = cls.new()
|
|
|
|
list = new.filter(**kwargs)
|
|
|
|
|
|
|
|
if as_class:
|
|
|
|
# Return a copy of the metadata list with overriden records
|
|
|
|
return cls(new.model, records=list)
|
|
|
|
else:
|
|
|
|
return list
|
|
|
|
|
|
|
|
# Instantiate the list with the proper class
|
|
|
|
@classmethod
|
|
|
|
def new(cls, /, *, force: bool = False) -> Self:
|
|
|
|
raise Exception('new() is not implemented for BrickMetadataList')
|
|
|
|
|
|
|
|
# URL to change the selected state of this metadata item for a set
|
|
|
|
@classmethod
|
|
|
|
def url_for_set_state(cls, id: str, /) -> str:
|
|
|
|
return url_for(
|
|
|
|
cls.set_state_endpoint,
|
|
|
|
id=id,
|
|
|
|
)
|