From 344d4fb5753d1fa8220168050b6b00a494653aa8 Mon Sep 17 00:00:00 2001 From: Gregoo Date: Thu, 30 Jan 2025 16:23:47 +0100 Subject: [PATCH] Metadata list --- bricktracker/metadata_list.py | 77 +++++++++++++++++++++++++++++++ bricktracker/reload.py | 3 +- bricktracker/set.py | 3 +- bricktracker/set_list.py | 5 +- bricktracker/set_status_list.py | 64 ++++--------------------- bricktracker/views/admin/admin.py | 2 +- bricktracker/views/index.py | 3 +- bricktracker/views/set.py | 7 +-- 8 files changed, 99 insertions(+), 65 deletions(-) create mode 100644 bricktracker/metadata_list.py diff --git a/bricktracker/metadata_list.py b/bricktracker/metadata_list.py new file mode 100644 index 0000000..80bb3df --- /dev/null +++ b/bricktracker/metadata_list.py @@ -0,0 +1,77 @@ +import logging +from typing import Type + +from .exceptions import NotFoundException +from .fields import BrickRecordFields +from .record_list import BrickRecordList +from .set_status import BrickSetStatus + +logger = logging.getLogger(__name__) + +T = BrickSetStatus + + +# 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 + + def __init__(self, model: Type[T], /, *, force: bool = False): + # Load statuses 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 statuses from the database + for record in self.select(): + status = model(record=record) + + self.__class__.records.append(status) + self.__class__.mapping[status.fields.id] = status + + # Return the items as columns for a select + def as_columns(self, /, **kwargs) -> str: + return ', '.join([ + '"{table}"."{column}"'.format( + table=self.table, + column=record.as_column(), + ) + for record + in self.filter(**kwargs) + ]) + + # Filter the list of records (this one does nothing) + def filter(self) -> list[T]: + return self.records + + # Grab a specific status + def get(self, id: str, /) -> T: + if id not in self.mapping: + raise NotFoundException( + '{kind} with ID {id} was not found in the database'.format( + kind=self.kind.capitalize(), + id=id, + ), + ) + + return self.mapping[id] + + # Get the list of statuses depending on the context + def list(self, /, **kwargs) -> list[T]: + return self.filter(**kwargs) diff --git a/bricktracker/reload.py b/bricktracker/reload.py index 28de7bf..259cffa 100644 --- a/bricktracker/reload.py +++ b/bricktracker/reload.py @@ -1,5 +1,6 @@ from .instructions_list import BrickInstructionsList from .retired_list import BrickRetiredList +from .set_status import BrickSetStatus from .set_status_list import BrickSetStatusList from .theme_list import BrickThemeList @@ -12,7 +13,7 @@ def reload() -> None: BrickInstructionsList(force=True) # Reload the set statuses - BrickSetStatusList(force=True) + BrickSetStatusList(BrickSetStatus, force=True) # Reload retired sets BrickRetiredList(force=True) diff --git a/bricktracker/set.py b/bricktracker/set.py index 1b11a94..32bf8da 100644 --- a/bricktracker/set.py +++ b/bricktracker/set.py @@ -9,6 +9,7 @@ from .exceptions import NotFoundException from .minifigure_list import BrickMinifigureList from .part_list import BrickPartList from .rebrickable_set import RebrickableSet +from .set_status import BrickSetStatus from .set_status_list import BrickSetStatusList from .sql import BrickSQL if TYPE_CHECKING: @@ -161,7 +162,7 @@ class BrickSet(RebrickableSet): # Load from database if not self.select( - statuses=BrickSetStatusList().as_columns(solo=True) + statuses=BrickSetStatusList(BrickSetStatus).as_columns(all=True) ): raise NotFoundException( 'Set with ID {id} was not found in the database'.format( diff --git a/bricktracker/set_list.py b/bricktracker/set_list.py index 47d31b2..251cfdc 100644 --- a/bricktracker/set_list.py +++ b/bricktracker/set_list.py @@ -3,6 +3,7 @@ from typing import Self from flask import current_app from .record_list import BrickRecordList +from .set_status import BrickSetStatus from .set_status_list import BrickSetStatusList from .set import BrickSet @@ -37,7 +38,7 @@ class BrickSetList(BrickRecordList[BrickSet]): # Load the sets from the database for record in self.select( order=self.order, - statuses=BrickSetStatusList().as_columns() + statuses=BrickSetStatusList(BrickSetStatus).as_columns() ): brickset = BrickSet(record=record) @@ -73,7 +74,7 @@ class BrickSetList(BrickRecordList[BrickSet]): for record in self.select( order=order, limit=limit, - statuses=BrickSetStatusList().as_columns() + statuses=BrickSetStatusList(BrickSetStatus).as_columns() ): brickset = BrickSet(record=record) diff --git a/bricktracker/set_status_list.py b/bricktracker/set_status_list.py index 12ce85a..b96f213 100644 --- a/bricktracker/set_status_list.py +++ b/bricktracker/set_status_list.py @@ -1,71 +1,23 @@ import logging -from .exceptions import NotFoundException -from .fields import BrickRecordFields -from .record_list import BrickRecordList +from .metadata_list import BrickMetadataList from .set_status import BrickSetStatus logger = logging.getLogger(__name__) # Lego sets status list -class BrickSetStatusList(BrickRecordList[BrickSetStatus]): - statuses: dict[str, BrickSetStatus] +class BrickSetStatusList(BrickMetadataList): + kind: str = 'set statuses' + + # Database table + table: str = 'bricktracker_set_statuses' # Queries select_query = 'set/metadata/status/list' - def __init__(self, /, *, force: bool = False): - # Load statuses 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 set statuses list') - - BrickSetStatusList.records = [] - BrickSetStatusList.statuses = {} - - # Load the statuses from the database - for record in self.select(): - status = BrickSetStatus(record=record) - - BrickSetStatusList.records.append(status) - BrickSetStatusList.statuses[status.fields.id] = status - - # Return the statuses as columns for a select - def as_columns( - self, - /, - *, - solo: bool = False, - table: str = 'bricktracker_set_statuses' - ) -> str: - return ', '.join([ - '"{table}"."{column}"'.format( - table=table, - column=record.as_column(), - ) - for record - in self.records - if solo or record.fields.displayed_on_grid - ]) - - # Grab a specific status - def get(self, id: str, /) -> BrickSetStatus: - if id not in self.statuses: - raise NotFoundException( - 'Status with ID {id} was not found in the database'.format( - id=id, - ), - ) - - return self.statuses[id] - - # Get the list of statuses depending on the context - def list(self, /, *, all: bool = False) -> list[BrickSetStatus]: + # Filter the list of set status + def filter(self, all: bool = False) -> list[BrickSetStatus]: return [ record for record diff --git a/bricktracker/views/admin/admin.py b/bricktracker/views/admin/admin.py index a7f8ce7..c18a74b 100644 --- a/bricktracker/views/admin/admin.py +++ b/bricktracker/views/admin/admin.py @@ -41,7 +41,7 @@ def admin() -> str: database_version = database.version database_counters = BrickSQL().count_records() - metadata_statuses = BrickSetStatusList().list(all=True) + metadata_statuses = BrickSetStatusList(BrickSetStatus).list(all=True) except Exception as e: database_exception = e diff --git a/bricktracker/views/index.py b/bricktracker/views/index.py index 3e3a880..f8fe7b7 100644 --- a/bricktracker/views/index.py +++ b/bricktracker/views/index.py @@ -2,6 +2,7 @@ from flask import Blueprint, render_template from .exceptions import exception_handler from ..minifigure_list import BrickMinifigureList +from ..set_status import BrickSetStatus from ..set_status_list import BrickSetStatusList from ..set_list import BrickSetList @@ -16,5 +17,5 @@ def index() -> str: 'index.html', brickset_collection=BrickSetList().last(), minifigure_collection=BrickMinifigureList().last(), - brickset_statuses=BrickSetStatusList().list(), + brickset_statuses=BrickSetStatusList(BrickSetStatus).list(), ) diff --git a/bricktracker/views/set.py b/bricktracker/views/set.py index 17fcff9..9f1990c 100644 --- a/bricktracker/views/set.py +++ b/bricktracker/views/set.py @@ -16,6 +16,7 @@ from .exceptions import exception_handler from ..minifigure import BrickMinifigure from ..part import BrickPart from ..set import BrickSet +from ..set_status import BrickSetStatus from ..set_status_list import BrickSetStatusList from ..set_list import BrickSetList from ..socket import MESSAGES @@ -32,7 +33,7 @@ def list() -> str: return render_template( 'sets.html', collection=BrickSetList().all(), - brickset_statuses=BrickSetStatusList().list(), + brickset_statuses=BrickSetStatusList(BrickSetStatus).list(), ) @@ -42,7 +43,7 @@ def list() -> str: @exception_handler(__file__, json=True) def update_status(*, id: str, metadata_id: str) -> Response: brickset = BrickSet().select_light(id) - status = BrickSetStatusList().get(metadata_id) + status = BrickSetStatusList(BrickSetStatus).get(metadata_id) state = status.update_set_state(brickset, request.json) @@ -97,7 +98,7 @@ def details(*, id: str) -> str: 'set.html', item=BrickSet().select_specific(id), open_instructions=request.args.get('open_instructions'), - brickset_statuses=BrickSetStatusList().list(all=True), + brickset_statuses=BrickSetStatusList(BrickSetStatus).list(all=True), )