forked from FrederikBaerentsen/BrickTracker
Set storage
This commit is contained in:
parent
ec7fab2a7a
commit
9aff7e622d
@ -34,6 +34,7 @@ Parts
|
|||||||
- Fix missing @login_required for set deletion
|
- Fix missing @login_required for set deletion
|
||||||
- Ownership
|
- Ownership
|
||||||
- Tags
|
- Tags
|
||||||
|
- Storage
|
||||||
|
|
||||||
- Socket
|
- Socket
|
||||||
- Add decorator for rebrickable, authenticated and threaded socket actions
|
- Add decorator for rebrickable, authenticated and threaded socket actions
|
||||||
@ -76,6 +77,7 @@ Parts
|
|||||||
- Ownership
|
- Ownership
|
||||||
- Tags
|
- Tags
|
||||||
- Refresh
|
- Refresh
|
||||||
|
- Storage
|
||||||
|
|
||||||
- Sets grid
|
- Sets grid
|
||||||
- Collapsible controls depending on screen size
|
- Collapsible controls depending on screen size
|
||||||
|
@ -19,6 +19,7 @@ from bricktracker.views.admin.instructions import admin_instructions_page
|
|||||||
from bricktracker.views.admin.owner import admin_owner_page
|
from bricktracker.views.admin.owner import admin_owner_page
|
||||||
from bricktracker.views.admin.retired import admin_retired_page
|
from bricktracker.views.admin.retired import admin_retired_page
|
||||||
from bricktracker.views.admin.status import admin_status_page
|
from bricktracker.views.admin.status import admin_status_page
|
||||||
|
from bricktracker.views.admin.storage import admin_storage_page
|
||||||
from bricktracker.views.admin.tag import admin_tag_page
|
from bricktracker.views.admin.tag import admin_tag_page
|
||||||
from bricktracker.views.admin.theme import admin_theme_page
|
from bricktracker.views.admin.theme import admin_theme_page
|
||||||
from bricktracker.views.error import error_404
|
from bricktracker.views.error import error_404
|
||||||
@ -86,6 +87,7 @@ def setup_app(app: Flask) -> None:
|
|||||||
app.register_blueprint(admin_retired_page)
|
app.register_blueprint(admin_retired_page)
|
||||||
app.register_blueprint(admin_owner_page)
|
app.register_blueprint(admin_owner_page)
|
||||||
app.register_blueprint(admin_status_page)
|
app.register_blueprint(admin_status_page)
|
||||||
|
app.register_blueprint(admin_storage_page)
|
||||||
app.register_blueprint(admin_tag_page)
|
app.register_blueprint(admin_tag_page)
|
||||||
app.register_blueprint(admin_theme_page)
|
app.register_blueprint(admin_theme_page)
|
||||||
|
|
||||||
|
@ -36,6 +36,9 @@ class BrickMetadata(BrickRecord):
|
|||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
# Defined an empty ID
|
||||||
|
self.fields.id = None
|
||||||
|
|
||||||
# Ingest the record if it has one
|
# Ingest the record if it has one
|
||||||
if record is not None:
|
if record is not None:
|
||||||
self.ingest(record)
|
self.ingest(record)
|
||||||
@ -129,8 +132,8 @@ class BrickMetadata(BrickRecord):
|
|||||||
json: Any | None = None,
|
json: Any | None = None,
|
||||||
value: Any | None = None
|
value: Any | None = None
|
||||||
) -> Any:
|
) -> Any:
|
||||||
if value is None:
|
if value is None and json is not None:
|
||||||
value = json.get('value', None) # type: ignore
|
value = json.get('value', None)
|
||||||
|
|
||||||
if value is None:
|
if value is None:
|
||||||
raise ErrorException('"{field}" of a {kind} cannot be set to an empty value'.format( # noqa: E501
|
raise ErrorException('"{field}" of a {kind} cannot be set to an empty value'.format( # noqa: E501
|
||||||
@ -180,16 +183,15 @@ class BrickMetadata(BrickRecord):
|
|||||||
/,
|
/,
|
||||||
*,
|
*,
|
||||||
json: Any | None = None,
|
json: Any | None = None,
|
||||||
state: bool | None = None,
|
state: Any | None = None
|
||||||
) -> Any:
|
) -> Any:
|
||||||
if state is None:
|
if state is None and json is not None:
|
||||||
state = json.get('value', False) # type: ignore
|
state = json.get('value', False)
|
||||||
|
|
||||||
parameters = self.sql_parameters()
|
parameters = self.sql_parameters()
|
||||||
parameters['set_id'] = brickset.fields.id
|
parameters['set_id'] = brickset.fields.id
|
||||||
parameters['state'] = state
|
parameters['state'] = state
|
||||||
|
|
||||||
# Update the status
|
|
||||||
rows, _ = BrickSQL().execute_and_commit(
|
rows, _ = BrickSQL().execute_and_commit(
|
||||||
self.update_set_state_query,
|
self.update_set_state_query,
|
||||||
parameters=parameters,
|
parameters=parameters,
|
||||||
@ -205,7 +207,53 @@ class BrickMetadata(BrickRecord):
|
|||||||
))
|
))
|
||||||
|
|
||||||
# Info
|
# Info
|
||||||
logger.info('{kind} "{name}" state change to "{state}" for set {set} ({id})'.format( # noqa: E501
|
logger.info('{kind} "{name}" state changed to "{state}" for set {set} ({id})'.format( # noqa: E501
|
||||||
|
kind=self.kind,
|
||||||
|
name=self.fields.name,
|
||||||
|
state=state,
|
||||||
|
set=brickset.fields.set,
|
||||||
|
id=brickset.fields.id,
|
||||||
|
))
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
# Update the selected value of this metadata item for a set
|
||||||
|
def update_set_value(
|
||||||
|
self,
|
||||||
|
brickset: 'BrickSet',
|
||||||
|
/,
|
||||||
|
*,
|
||||||
|
json: Any | None = None,
|
||||||
|
state: Any | None = None,
|
||||||
|
) -> Any:
|
||||||
|
if state is None and json is not None:
|
||||||
|
state = json.get('value', '')
|
||||||
|
|
||||||
|
if state == '':
|
||||||
|
state = None
|
||||||
|
|
||||||
|
parameters = self.sql_parameters()
|
||||||
|
parameters['set_id'] = brickset.fields.id
|
||||||
|
parameters['state'] = state
|
||||||
|
|
||||||
|
rows, _ = BrickSQL().execute_and_commit(
|
||||||
|
self.update_set_state_query,
|
||||||
|
parameters=parameters,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update the status
|
||||||
|
if state is None and not hasattr(self.fields, 'name'):
|
||||||
|
self.fields.name = 'None'
|
||||||
|
|
||||||
|
if rows != 1:
|
||||||
|
raise DatabaseException('Could not update the {kind} value for set {set} ({id})'.format( # noqa: E501
|
||||||
|
kind=self.kind,
|
||||||
|
set=brickset.fields.set,
|
||||||
|
id=brickset.fields.id,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Info
|
||||||
|
logger.info('{kind} value changed to "{name}" ({state}) for set {set} ({id})'.format( # noqa: E501
|
||||||
kind=self.kind,
|
kind=self.kind,
|
||||||
name=self.fields.name,
|
name=self.fields.name,
|
||||||
state=state,
|
state=state,
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Type, TypeVar
|
from typing import List, overload, Self, Type, TypeVar
|
||||||
|
|
||||||
|
from flask import url_for
|
||||||
|
|
||||||
from .exceptions import NotFoundException
|
from .exceptions import NotFoundException
|
||||||
from .fields import BrickRecordFields
|
from .fields import BrickRecordFields
|
||||||
from .record_list import BrickRecordList
|
from .record_list import BrickRecordList
|
||||||
from .set_owner import BrickSetOwner
|
from .set_owner import BrickSetOwner
|
||||||
from .set_status import BrickSetStatus
|
from .set_status import BrickSetStatus
|
||||||
|
from .set_storage import BrickSetStorage
|
||||||
from .set_tag import BrickSetTag
|
from .set_tag import BrickSetTag
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
T = TypeVar('T', BrickSetStatus, BrickSetOwner, BrickSetTag)
|
T = TypeVar('T', BrickSetOwner, BrickSetStatus, BrickSetStorage, BrickSetTag)
|
||||||
|
|
||||||
|
|
||||||
# Lego sets metadata list
|
# Lego sets metadata list
|
||||||
@ -25,55 +28,119 @@ class BrickMetadataList(BrickRecordList[T]):
|
|||||||
# Queries
|
# Queries
|
||||||
select_query: str
|
select_query: str
|
||||||
|
|
||||||
def __init__(self, model: Type[T], /, *, force: bool = False):
|
# Set state endpoint
|
||||||
# Load statuses only if there is none already loaded
|
set_state_endpoint: str
|
||||||
records = getattr(self, 'records', None)
|
|
||||||
|
|
||||||
if records is None or force:
|
def __init__(
|
||||||
# Don't use super()__init__ as it would mask class variables
|
self,
|
||||||
self.fields = BrickRecordFields()
|
model: Type[T],
|
||||||
|
/,
|
||||||
|
*,
|
||||||
|
force: bool = False,
|
||||||
|
records: list[T] | None = None
|
||||||
|
):
|
||||||
|
self.model = model
|
||||||
|
|
||||||
logger.info('Loading {kind} list'.format(
|
# Records override (masking the class variables with instance ones)
|
||||||
kind=self.kind
|
if records is not None:
|
||||||
))
|
self.records = []
|
||||||
|
self.mapping = {}
|
||||||
|
|
||||||
self.__class__.records = []
|
for metadata in records:
|
||||||
self.__class__.mapping = {}
|
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)
|
||||||
|
|
||||||
# Load the statuses from the database
|
if records is None or force:
|
||||||
for record in self.select():
|
# Don't use super()__init__ as it would mask class variables
|
||||||
status = model(record=record)
|
self.fields = BrickRecordFields()
|
||||||
|
|
||||||
self.__class__.records.append(status)
|
logger.info('Loading {kind} list'.format(
|
||||||
self.__class__.mapping[status.fields.id] = status
|
kind=self.kind
|
||||||
|
))
|
||||||
|
|
||||||
# Return the items as columns for a select
|
self.__class__.records = []
|
||||||
def as_columns(self, /, **kwargs) -> str:
|
self.__class__.mapping = {}
|
||||||
return ', '.join([
|
|
||||||
'"{table}"."{column}"'.format(
|
# Load the metadata from the database
|
||||||
table=self.table,
|
for record in self.select():
|
||||||
column=record.as_column(),
|
metadata = model(record=record)
|
||||||
)
|
|
||||||
for record
|
self.__class__.records.append(metadata)
|
||||||
in self.filter(**kwargs)
|
self.__class__.mapping[metadata.fields.id] = metadata
|
||||||
])
|
|
||||||
|
# HTML prefix name
|
||||||
|
def as_prefix(self, /) -> str:
|
||||||
|
return self.kind.replace(' ', '-')
|
||||||
|
|
||||||
# Filter the list of records (this one does nothing)
|
# Filter the list of records (this one does nothing)
|
||||||
def filter(self) -> list[T]:
|
def filter(self) -> list[T]:
|
||||||
return self.records
|
return self.records
|
||||||
|
|
||||||
|
# Return the items as columns for a select
|
||||||
|
@classmethod
|
||||||
|
def as_columns(cls, /, **kwargs) -> str:
|
||||||
|
new = cls.new()
|
||||||
|
|
||||||
|
return ', '.join([
|
||||||
|
'"{table}"."{column}"'.format(
|
||||||
|
table=cls.table,
|
||||||
|
column=record.as_column(),
|
||||||
|
)
|
||||||
|
for record
|
||||||
|
in new.filter(**kwargs)
|
||||||
|
])
|
||||||
|
|
||||||
# Grab a specific status
|
# Grab a specific status
|
||||||
def get(self, id: str, /) -> T:
|
@classmethod
|
||||||
if id not in self.mapping:
|
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:
|
||||||
raise NotFoundException(
|
raise NotFoundException(
|
||||||
'{kind} with ID {id} was not found in the database'.format(
|
'{kind} with ID {id} was not found in the database'.format(
|
||||||
kind=self.kind.capitalize(),
|
kind=new.kind.capitalize(),
|
||||||
id=id,
|
id=id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.mapping[id]
|
return new.mapping[id]
|
||||||
|
|
||||||
# Get the list of statuses depending on the context
|
# Get the list of statuses depending on the context
|
||||||
def list(self, /, **kwargs) -> list[T]:
|
@overload
|
||||||
return self.filter(**kwargs)
|
@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:
|
||||||
|
print(list)
|
||||||
|
# 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,
|
||||||
|
)
|
||||||
|
@ -10,17 +10,19 @@ if TYPE_CHECKING:
|
|||||||
from .set import BrickSet
|
from .set import BrickSet
|
||||||
from .set_owner import BrickSetOwner
|
from .set_owner import BrickSetOwner
|
||||||
from .set_status import BrickSetStatus
|
from .set_status import BrickSetStatus
|
||||||
|
from .set_storage import BrickSetStorage
|
||||||
from .set_tag import BrickSetTag
|
from .set_tag import BrickSetTag
|
||||||
from .wish import BrickWish
|
from .wish import BrickWish
|
||||||
|
|
||||||
T = TypeVar(
|
T = TypeVar(
|
||||||
'T',
|
'T',
|
||||||
|
'BrickMinifigure',
|
||||||
|
'BrickPart',
|
||||||
'BrickSet',
|
'BrickSet',
|
||||||
'BrickSetOwner',
|
'BrickSetOwner',
|
||||||
'BrickSetStatus',
|
'BrickSetStatus',
|
||||||
|
'BrickSetStorage',
|
||||||
'BrickSetTag',
|
'BrickSetTag',
|
||||||
'BrickPart',
|
|
||||||
'BrickMinifigure',
|
|
||||||
'BrickWish',
|
'BrickWish',
|
||||||
'RebrickableSet'
|
'RebrickableSet'
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ from .instructions_list import BrickInstructionsList
|
|||||||
from .retired_list import BrickRetiredList
|
from .retired_list import BrickRetiredList
|
||||||
from .set_owner_list import BrickSetOwnerList
|
from .set_owner_list import BrickSetOwnerList
|
||||||
from .set_status_list import BrickSetStatusList
|
from .set_status_list import BrickSetStatusList
|
||||||
|
from .set_storage_list import BrickSetStorageList
|
||||||
from .set_tag_list import BrickSetTagList
|
from .set_tag_list import BrickSetTagList
|
||||||
from .theme_list import BrickThemeList
|
from .theme_list import BrickThemeList
|
||||||
|
|
||||||
@ -19,6 +20,9 @@ def reload() -> None:
|
|||||||
# Reload the set statuses
|
# Reload the set statuses
|
||||||
BrickSetStatusList.new(force=True)
|
BrickSetStatusList.new(force=True)
|
||||||
|
|
||||||
|
# Reload the set storages
|
||||||
|
BrickSetStorageList.new(force=True)
|
||||||
|
|
||||||
# Reload the set tags
|
# Reload the set tags
|
||||||
BrickSetTagList.new(force=True)
|
BrickSetTagList.new(force=True)
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ from .part_list import BrickPartList
|
|||||||
from .rebrickable_set import RebrickableSet
|
from .rebrickable_set import RebrickableSet
|
||||||
from .set_owner_list import BrickSetOwnerList
|
from .set_owner_list import BrickSetOwnerList
|
||||||
from .set_status_list import BrickSetStatusList
|
from .set_status_list import BrickSetStatusList
|
||||||
|
from .set_storage_list import BrickSetStorageList
|
||||||
from .set_tag_list import BrickSetTagList
|
from .set_tag_list import BrickSetTagList
|
||||||
from .sql import BrickSQL
|
from .sql import BrickSQL
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -55,9 +56,30 @@ class BrickSet(RebrickableSet):
|
|||||||
self.fields.id = str(uuid4())
|
self.fields.id = str(uuid4())
|
||||||
|
|
||||||
if not refresh:
|
if not refresh:
|
||||||
|
# Save the storage
|
||||||
|
storage = BrickSetStorageList.get(
|
||||||
|
data.get('storage', ''),
|
||||||
|
allow_none=True
|
||||||
|
)
|
||||||
|
self.fields.storage = storage.fields.id
|
||||||
|
|
||||||
# Insert into database
|
# Insert into database
|
||||||
self.insert(commit=False)
|
self.insert(commit=False)
|
||||||
|
|
||||||
|
# Save the owners
|
||||||
|
owners: list[str] = list(data.get('owners', []))
|
||||||
|
|
||||||
|
for id in owners:
|
||||||
|
owner = BrickSetOwnerList.get(id)
|
||||||
|
owner.update_set_state(self, state=True)
|
||||||
|
|
||||||
|
# Save the tags
|
||||||
|
tags: list[str] = list(data.get('tags', []))
|
||||||
|
|
||||||
|
for id in tags:
|
||||||
|
tag = BrickSetTagList.get(id)
|
||||||
|
tag.update_set_state(self, state=True)
|
||||||
|
|
||||||
# Insert the rebrickable set into database
|
# Insert the rebrickable set into database
|
||||||
self.insert_rebrickable()
|
self.insert_rebrickable()
|
||||||
|
|
||||||
@ -69,20 +91,6 @@ class BrickSet(RebrickableSet):
|
|||||||
if not BrickMinifigureList.download(socket, self, refresh=refresh):
|
if not BrickMinifigureList.download(socket, self, refresh=refresh):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Save the owners
|
|
||||||
owners: list[str] = list(data.get('owners', []))
|
|
||||||
|
|
||||||
for id in owners:
|
|
||||||
owner = BrickSetOwnerList(BrickSetOwner).get(id)
|
|
||||||
owner.update_set_state(self, state=True)
|
|
||||||
|
|
||||||
# Save the tags
|
|
||||||
tags: list[str] = list(data.get('tags', []))
|
|
||||||
|
|
||||||
for id in tags:
|
|
||||||
tag = BrickSetTagList(BrickSetTag).get(id)
|
|
||||||
tag.update_set_state(self, state=True)
|
|
||||||
|
|
||||||
# Commit the transaction to the database
|
# Commit the transaction to the database
|
||||||
socket.auto_progress(
|
socket.auto_progress(
|
||||||
message='Set {set}: writing to the database'.format(
|
message='Set {set}: writing to the database'.format(
|
||||||
@ -166,9 +174,9 @@ class BrickSet(RebrickableSet):
|
|||||||
|
|
||||||
# Load from database
|
# Load from database
|
||||||
if not self.select(
|
if not self.select(
|
||||||
owners=BrickSetOwnerList.new().as_columns(),
|
owners=BrickSetOwnerList.as_columns(),
|
||||||
statuses=BrickSetStatusList.new().as_columns(all=True),
|
statuses=BrickSetStatusList.as_columns(all=True),
|
||||||
tags=BrickSetTagList.new().as_columns(),
|
tags=BrickSetTagList.as_columns(),
|
||||||
):
|
):
|
||||||
raise NotFoundException(
|
raise NotFoundException(
|
||||||
'Set with ID {id} was not found in the database'.format(
|
'Set with ID {id} was not found in the database'.format(
|
||||||
|
@ -41,9 +41,9 @@ class BrickSetList(BrickRecordList[BrickSet]):
|
|||||||
# Load the sets from the database
|
# Load the sets from the database
|
||||||
for record in self.select(
|
for record in self.select(
|
||||||
order=self.order,
|
order=self.order,
|
||||||
owners=BrickSetOwnerList.new().as_columns(),
|
owners=BrickSetOwnerList.as_columns(),
|
||||||
statuses=BrickSetStatusList.new().as_columns(),
|
statuses=BrickSetStatusList.as_columns(),
|
||||||
tags=BrickSetTagList.new().as_columns(),
|
tags=BrickSetTagList.as_columns(),
|
||||||
):
|
):
|
||||||
brickset = BrickSet(record=record)
|
brickset = BrickSet(record=record)
|
||||||
|
|
||||||
@ -112,9 +112,9 @@ class BrickSetList(BrickRecordList[BrickSet]):
|
|||||||
for record in self.select(
|
for record in self.select(
|
||||||
order=order,
|
order=order,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
owners=BrickSetOwnerList.new().as_columns(),
|
owners=BrickSetOwnerList.as_columns(),
|
||||||
statuses=BrickSetStatusList.new().as_columns(),
|
statuses=BrickSetStatusList.as_columns(),
|
||||||
tags=BrickSetTagList.new().as_columns(),
|
tags=BrickSetTagList.as_columns(),
|
||||||
):
|
):
|
||||||
brickset = BrickSet(record=record)
|
brickset = BrickSet(record=record)
|
||||||
|
|
||||||
|
13
bricktracker/set_storage.py
Normal file
13
bricktracker/set_storage.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from .metadata import BrickMetadata
|
||||||
|
|
||||||
|
|
||||||
|
# Lego set storage metadata
|
||||||
|
class BrickSetStorage(BrickMetadata):
|
||||||
|
kind: str = 'storage'
|
||||||
|
|
||||||
|
# Queries
|
||||||
|
delete_query: str = 'set/metadata/storage/delete'
|
||||||
|
insert_query: str = 'set/metadata/storage/insert'
|
||||||
|
select_query: str = 'set/metadata/storage/select'
|
||||||
|
update_field_query: str = 'set/metadata/storage/update/field'
|
||||||
|
update_set_state_query: str = 'set/metadata/storage/update/state'
|
23
bricktracker/set_storage_list.py
Normal file
23
bricktracker/set_storage_list.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import logging
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
|
from .metadata_list import BrickMetadataList
|
||||||
|
from .set_storage import BrickSetStorage
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Lego sets storage list
|
||||||
|
class BrickSetStorageList(BrickMetadataList[BrickSetStorage]):
|
||||||
|
kind: str = 'set storages'
|
||||||
|
|
||||||
|
# Queries
|
||||||
|
select_query = 'set/metadata/storage/list'
|
||||||
|
|
||||||
|
# Set state endpoint
|
||||||
|
set_state_endpoint: str = 'set.update_storage'
|
||||||
|
|
||||||
|
# Instantiate the list with the proper class
|
||||||
|
@classmethod
|
||||||
|
def new(cls, /, *, force: bool = False) -> Self:
|
||||||
|
return cls(BrickSetStorage, force=force)
|
@ -1,5 +1,6 @@
|
|||||||
SELECT
|
SELECT
|
||||||
{% block id %}{% endblock %}
|
{% block id %}{% endblock %}
|
||||||
|
"bricktracker_sets"."storage",
|
||||||
"rebrickable_sets"."set",
|
"rebrickable_sets"."set",
|
||||||
"rebrickable_sets"."number",
|
"rebrickable_sets"."number",
|
||||||
"rebrickable_sets"."version",
|
"rebrickable_sets"."version",
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
INSERT OR IGNORE INTO "bricktracker_sets" (
|
INSERT OR IGNORE INTO "bricktracker_sets" (
|
||||||
"id",
|
"id",
|
||||||
"set"
|
"set",
|
||||||
|
"storage"
|
||||||
) VALUES (
|
) VALUES (
|
||||||
:id,
|
:id,
|
||||||
:set
|
:set,
|
||||||
|
:storage
|
||||||
)
|
)
|
||||||
|
6
bricktracker/sql/set/metadata/storage/base.sql
Normal file
6
bricktracker/sql/set/metadata/storage/base.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
SELECT
|
||||||
|
"bricktracker_metadata_storages"."id",
|
||||||
|
"bricktracker_metadata_storages"."name"
|
||||||
|
FROM "bricktracker_metadata_storages"
|
||||||
|
|
||||||
|
{% block where %}{% endblock %}
|
6
bricktracker/sql/set/metadata/storage/delete.sql
Normal file
6
bricktracker/sql/set/metadata/storage/delete.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
DELETE FROM "bricktracker_metadata_storages"
|
||||||
|
WHERE "bricktracker_metadata_statuses"."id" IS NOT DISTINCT FROM '{{ id }}';
|
||||||
|
|
||||||
|
COMMIT;
|
11
bricktracker/sql/set/metadata/storage/insert.sql
Normal file
11
bricktracker/sql/set/metadata/storage/insert.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
INSERT INTO "bricktracker_metadata_storages" (
|
||||||
|
"id",
|
||||||
|
"name"
|
||||||
|
) VALUES (
|
||||||
|
'{{ id }}',
|
||||||
|
'{{ name }}'
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
1
bricktracker/sql/set/metadata/storage/list.sql
Normal file
1
bricktracker/sql/set/metadata/storage/list.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% extends 'set/metadata/storage/base.sql' %}
|
5
bricktracker/sql/set/metadata/storage/select.sql
Normal file
5
bricktracker/sql/set/metadata/storage/select.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends 'set/metadata/storage/base.sql' %}
|
||||||
|
|
||||||
|
{% block where %}
|
||||||
|
WHERE "bricktracker_metadata_storages"."id" IS NOT DISTINCT FROM :id
|
||||||
|
{% endblock %}
|
3
bricktracker/sql/set/metadata/storage/update/field.sql
Normal file
3
bricktracker/sql/set/metadata/storage/update/field.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
UPDATE "bricktracker_metadata_storages"
|
||||||
|
SET "{{field}}" = :value
|
||||||
|
WHERE "bricktracker_metadata_storages"."id" IS NOT DISTINCT FROM :id
|
3
bricktracker/sql/set/metadata/storage/update/state.sql
Normal file
3
bricktracker/sql/set/metadata/storage/update/state.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
UPDATE "bricktracker_sets"
|
||||||
|
SET "storage" = :state
|
||||||
|
WHERE "bricktracker_sets"."id" IS NOT DISTINCT FROM :set_id
|
@ -4,6 +4,7 @@ from flask_login import login_required
|
|||||||
from ..configuration_list import BrickConfigurationList
|
from ..configuration_list import BrickConfigurationList
|
||||||
from .exceptions import exception_handler
|
from .exceptions import exception_handler
|
||||||
from ..set_owner_list import BrickSetOwnerList
|
from ..set_owner_list import BrickSetOwnerList
|
||||||
|
from ..set_storage_list import BrickSetStorageList
|
||||||
from ..set_tag_list import BrickSetTagList
|
from ..set_tag_list import BrickSetTagList
|
||||||
from ..socket import MESSAGES
|
from ..socket import MESSAGES
|
||||||
|
|
||||||
@ -19,8 +20,9 @@ def add() -> str:
|
|||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'add.html',
|
'add.html',
|
||||||
brickset_owners=BrickSetOwnerList.new().list(),
|
brickset_owners=BrickSetOwnerList.list(),
|
||||||
brickset_tags=BrickSetTagList.new().list(),
|
brickset_storages=BrickSetStorageList.list(),
|
||||||
|
brickset_tags=BrickSetTagList.list(),
|
||||||
path=current_app.config['SOCKET_PATH'],
|
path=current_app.config['SOCKET_PATH'],
|
||||||
namespace=current_app.config['SOCKET_NAMESPACE'],
|
namespace=current_app.config['SOCKET_NAMESPACE'],
|
||||||
messages=MESSAGES
|
messages=MESSAGES
|
||||||
@ -36,8 +38,9 @@ def bulk() -> str:
|
|||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'add.html',
|
'add.html',
|
||||||
brickset_owners=BrickSetOwnerList.new().list(),
|
brickset_owners=BrickSetOwnerList.list(),
|
||||||
brickset_tags=BrickSetTagList.new().list(),
|
brickset_storages=BrickSetStorageList.list(),
|
||||||
|
brickset_tags=BrickSetTagList.list(),
|
||||||
path=current_app.config['SOCKET_PATH'],
|
path=current_app.config['SOCKET_PATH'],
|
||||||
namespace=current_app.config['SOCKET_NAMESPACE'],
|
namespace=current_app.config['SOCKET_NAMESPACE'],
|
||||||
messages=MESSAGES,
|
messages=MESSAGES,
|
||||||
|
@ -10,6 +10,8 @@ from ...rebrickable_image import RebrickableImage
|
|||||||
from ...retired_list import BrickRetiredList
|
from ...retired_list import BrickRetiredList
|
||||||
from ...set_owner import BrickSetOwner
|
from ...set_owner import BrickSetOwner
|
||||||
from ...set_owner_list import BrickSetOwnerList
|
from ...set_owner_list import BrickSetOwnerList
|
||||||
|
from ...set_storage import BrickSetStorage
|
||||||
|
from ...set_storage_list import BrickSetStorageList
|
||||||
from ...set_status import BrickSetStatus
|
from ...set_status import BrickSetStatus
|
||||||
from ...set_status_list import BrickSetStatusList
|
from ...set_status_list import BrickSetStatusList
|
||||||
from ...set_tag import BrickSetTag
|
from ...set_tag import BrickSetTag
|
||||||
@ -34,6 +36,7 @@ def admin() -> str:
|
|||||||
database_version: int = -1
|
database_version: int = -1
|
||||||
metadata_owners: list[BrickSetOwner] = []
|
metadata_owners: list[BrickSetOwner] = []
|
||||||
metadata_statuses: list[BrickSetStatus] = []
|
metadata_statuses: list[BrickSetStatus] = []
|
||||||
|
metadata_storages: list[BrickSetStorage] = []
|
||||||
metadata_tags: list[BrickSetTag] = []
|
metadata_tags: list[BrickSetTag] = []
|
||||||
nil_minifigure_name: str = ''
|
nil_minifigure_name: str = ''
|
||||||
nil_minifigure_url: str = ''
|
nil_minifigure_url: str = ''
|
||||||
@ -47,9 +50,10 @@ def admin() -> str:
|
|||||||
database_version = database.version
|
database_version = database.version
|
||||||
database_counters = BrickSQL().count_records()
|
database_counters = BrickSQL().count_records()
|
||||||
|
|
||||||
metadata_owners = BrickSetOwnerList.new().list()
|
metadata_owners = BrickSetOwnerList.list()
|
||||||
metadata_statuses = BrickSetStatusList.new().list(all=True)
|
metadata_statuses = BrickSetStatusList.list(all=True)
|
||||||
metadata_tags = BrickSetTagList.new().list()
|
metadata_storages = BrickSetStorageList.list()
|
||||||
|
metadata_tags = BrickSetTagList.list()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
database_exception = e
|
database_exception = e
|
||||||
|
|
||||||
@ -76,6 +80,7 @@ def admin() -> str:
|
|||||||
open_owner = request.args.get('open_owner', None)
|
open_owner = request.args.get('open_owner', None)
|
||||||
open_retired = request.args.get('open_retired', None)
|
open_retired = request.args.get('open_retired', None)
|
||||||
open_status = request.args.get('open_status', None)
|
open_status = request.args.get('open_status', None)
|
||||||
|
open_storage = request.args.get('open_storage', None)
|
||||||
open_tag = request.args.get('open_tag', None)
|
open_tag = request.args.get('open_tag', None)
|
||||||
open_theme = request.args.get('open_theme', None)
|
open_theme = request.args.get('open_theme', None)
|
||||||
|
|
||||||
@ -86,6 +91,7 @@ def admin() -> str:
|
|||||||
open_owner is None and
|
open_owner is None and
|
||||||
open_retired is None and
|
open_retired is None and
|
||||||
open_status is None and
|
open_status is None and
|
||||||
|
open_storage is None and
|
||||||
open_tag is None and
|
open_tag is None and
|
||||||
open_theme is None
|
open_theme is None
|
||||||
)
|
)
|
||||||
@ -101,6 +107,7 @@ def admin() -> str:
|
|||||||
instructions=BrickInstructionsList(),
|
instructions=BrickInstructionsList(),
|
||||||
metadata_owners=metadata_owners,
|
metadata_owners=metadata_owners,
|
||||||
metadata_statuses=metadata_statuses,
|
metadata_statuses=metadata_statuses,
|
||||||
|
metadata_storages=metadata_storages,
|
||||||
metadata_tags=metadata_tags,
|
metadata_tags=metadata_tags,
|
||||||
nil_minifigure_name=nil_minifigure_name,
|
nil_minifigure_name=nil_minifigure_name,
|
||||||
nil_minifigure_url=nil_minifigure_url,
|
nil_minifigure_url=nil_minifigure_url,
|
||||||
@ -113,11 +120,13 @@ def admin() -> str:
|
|||||||
open_owner=open_owner,
|
open_owner=open_owner,
|
||||||
open_retired=open_retired,
|
open_retired=open_retired,
|
||||||
open_status=open_status,
|
open_status=open_status,
|
||||||
|
open_storage=open_storage,
|
||||||
open_tag=open_tag,
|
open_tag=open_tag,
|
||||||
open_theme=open_theme,
|
open_theme=open_theme,
|
||||||
owner_error=request.args.get('owner_error'),
|
owner_error=request.args.get('owner_error'),
|
||||||
retired=BrickRetiredList(),
|
retired=BrickRetiredList(),
|
||||||
status_error=request.args.get('status_error'),
|
status_error=request.args.get('status_error'),
|
||||||
|
storage_error=request.args.get('storage_error'),
|
||||||
tag_error=request.args.get('tag_error'),
|
tag_error=request.args.get('tag_error'),
|
||||||
theme=BrickThemeList(),
|
theme=BrickThemeList(),
|
||||||
)
|
)
|
||||||
|
84
bricktracker/views/admin/storage.py
Normal file
84
bricktracker/views/admin/storage.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
from flask import (
|
||||||
|
Blueprint,
|
||||||
|
redirect,
|
||||||
|
request,
|
||||||
|
render_template,
|
||||||
|
url_for,
|
||||||
|
)
|
||||||
|
from flask_login import login_required
|
||||||
|
from werkzeug.wrappers.response import Response
|
||||||
|
|
||||||
|
from ..exceptions import exception_handler
|
||||||
|
from ...reload import reload
|
||||||
|
from ...set_storage import BrickSetStorage
|
||||||
|
|
||||||
|
admin_storage_page = Blueprint(
|
||||||
|
'admin_storage',
|
||||||
|
__name__,
|
||||||
|
url_prefix='/admin/storage'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Add a metadata storage
|
||||||
|
@admin_storage_page.route('/add', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(
|
||||||
|
__file__,
|
||||||
|
post_redirect='admin.admin',
|
||||||
|
error_name='storage_error',
|
||||||
|
open_storage=True
|
||||||
|
)
|
||||||
|
def add() -> Response:
|
||||||
|
BrickSetStorage().from_form(request.form).insert()
|
||||||
|
|
||||||
|
reload()
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_storage=True))
|
||||||
|
|
||||||
|
|
||||||
|
# Delete the metadata storage
|
||||||
|
@admin_storage_page.route('<id>/delete', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def delete(*, id: str) -> str:
|
||||||
|
return render_template(
|
||||||
|
'admin.html',
|
||||||
|
delete_storage=True,
|
||||||
|
storage=BrickSetStorage().select_specific(id),
|
||||||
|
error=request.args.get('storage_error')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Actually delete the metadata storage
|
||||||
|
@admin_storage_page.route('<id>/delete', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(
|
||||||
|
__file__,
|
||||||
|
post_redirect='admin_storage.delete',
|
||||||
|
error_name='storage_error'
|
||||||
|
)
|
||||||
|
def do_delete(*, id: str) -> Response:
|
||||||
|
storage = BrickSetStorage().select_specific(id)
|
||||||
|
storage.delete()
|
||||||
|
|
||||||
|
reload()
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_storage=True))
|
||||||
|
|
||||||
|
|
||||||
|
# Rename the metadata storage
|
||||||
|
@admin_storage_page.route('<id>/rename', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(
|
||||||
|
__file__,
|
||||||
|
post_redirect='admin.admin',
|
||||||
|
error_name='storage_error',
|
||||||
|
open_storage=True
|
||||||
|
)
|
||||||
|
def rename(*, id: str) -> Response:
|
||||||
|
storage = BrickSetStorage().select_specific(id)
|
||||||
|
storage.from_form(request.form).rename()
|
||||||
|
|
||||||
|
reload()
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_storage=True))
|
@ -4,6 +4,7 @@ from .exceptions import exception_handler
|
|||||||
from ..minifigure_list import BrickMinifigureList
|
from ..minifigure_list import BrickMinifigureList
|
||||||
from ..set_owner_list import BrickSetOwnerList
|
from ..set_owner_list import BrickSetOwnerList
|
||||||
from ..set_status_list import BrickSetStatusList
|
from ..set_status_list import BrickSetStatusList
|
||||||
|
from ..set_storage_list import BrickSetStorageList
|
||||||
from ..set_tag_list import BrickSetTagList
|
from ..set_tag_list import BrickSetTagList
|
||||||
from ..set_list import BrickSetList
|
from ..set_list import BrickSetList
|
||||||
|
|
||||||
@ -17,8 +18,9 @@ def index() -> str:
|
|||||||
return render_template(
|
return render_template(
|
||||||
'index.html',
|
'index.html',
|
||||||
brickset_collection=BrickSetList().last(),
|
brickset_collection=BrickSetList().last(),
|
||||||
brickset_owners=BrickSetOwnerList.new().list(),
|
brickset_owners=BrickSetOwnerList.list(),
|
||||||
brickset_statuses=BrickSetStatusList.new().list(),
|
brickset_statuses=BrickSetStatusList.list(),
|
||||||
brickset_tags=BrickSetTagList.new().list(),
|
brickset_storages=BrickSetStorageList.list(as_class=True),
|
||||||
|
brickset_tags=BrickSetTagList.list(),
|
||||||
minifigure_collection=BrickMinifigureList().last(),
|
minifigure_collection=BrickMinifigureList().last(),
|
||||||
)
|
)
|
||||||
|
@ -19,6 +19,7 @@ from ..set import BrickSet
|
|||||||
from ..set_list import BrickSetList
|
from ..set_list import BrickSetList
|
||||||
from ..set_owner_list import BrickSetOwnerList
|
from ..set_owner_list import BrickSetOwnerList
|
||||||
from ..set_status_list import BrickSetStatusList
|
from ..set_status_list import BrickSetStatusList
|
||||||
|
from ..set_storage_list import BrickSetStorageList
|
||||||
from ..set_tag_list import BrickSetTagList
|
from ..set_tag_list import BrickSetTagList
|
||||||
from ..socket import MESSAGES
|
from ..socket import MESSAGES
|
||||||
|
|
||||||
@ -34,9 +35,10 @@ def list() -> str:
|
|||||||
return render_template(
|
return render_template(
|
||||||
'sets.html',
|
'sets.html',
|
||||||
collection=BrickSetList().all(),
|
collection=BrickSetList().all(),
|
||||||
brickset_owners=BrickSetOwnerList.new().list(),
|
brickset_owners=BrickSetOwnerList.list(),
|
||||||
brickset_statuses=BrickSetStatusList.new().list(),
|
brickset_statuses=BrickSetStatusList.list(),
|
||||||
brickset_tags=BrickSetTagList.new().list(),
|
brickset_storages=BrickSetStorageList.list(as_class=True),
|
||||||
|
brickset_tags=BrickSetTagList.list(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -46,7 +48,7 @@ def list() -> str:
|
|||||||
@exception_handler(__file__, json=True)
|
@exception_handler(__file__, json=True)
|
||||||
def update_owner(*, id: str, metadata_id: str) -> Response:
|
def update_owner(*, id: str, metadata_id: str) -> Response:
|
||||||
brickset = BrickSet().select_light(id)
|
brickset = BrickSet().select_light(id)
|
||||||
owner = BrickSetOwnerList.new().get(metadata_id)
|
owner = BrickSetOwnerList.get(metadata_id)
|
||||||
|
|
||||||
state = owner.update_set_state(brickset, json=request.json)
|
state = owner.update_set_state(brickset, json=request.json)
|
||||||
|
|
||||||
@ -59,20 +61,36 @@ def update_owner(*, id: str, metadata_id: str) -> Response:
|
|||||||
@exception_handler(__file__, json=True)
|
@exception_handler(__file__, json=True)
|
||||||
def update_status(*, id: str, metadata_id: str) -> Response:
|
def update_status(*, id: str, metadata_id: str) -> Response:
|
||||||
brickset = BrickSet().select_light(id)
|
brickset = BrickSet().select_light(id)
|
||||||
status = BrickSetStatusList.new().get(metadata_id)
|
status = BrickSetStatusList.get(metadata_id)
|
||||||
|
|
||||||
state = status.update_set_state(brickset, json=request.json)
|
state = status.update_set_state(brickset, json=request.json)
|
||||||
|
|
||||||
return jsonify({'value': state})
|
return jsonify({'value': state})
|
||||||
|
|
||||||
|
|
||||||
|
# Change the state of a storage
|
||||||
|
@set_page.route('/<id>/storage', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, json=True)
|
||||||
|
def update_storage(*, id: str) -> Response:
|
||||||
|
brickset = BrickSet().select_light(id)
|
||||||
|
storage = BrickSetStorageList.get(
|
||||||
|
request.json.get('value', ''), # type: ignore
|
||||||
|
allow_none=True
|
||||||
|
)
|
||||||
|
|
||||||
|
state = storage.update_set_value(brickset, state=storage.fields.id)
|
||||||
|
|
||||||
|
return jsonify({'value': state})
|
||||||
|
|
||||||
|
|
||||||
# Change the state of a tag
|
# Change the state of a tag
|
||||||
@set_page.route('/<id>/tag/<metadata_id>', methods=['POST'])
|
@set_page.route('/<id>/tag/<metadata_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@exception_handler(__file__, json=True)
|
@exception_handler(__file__, json=True)
|
||||||
def update_tag(*, id: str, metadata_id: str) -> Response:
|
def update_tag(*, id: str, metadata_id: str) -> Response:
|
||||||
brickset = BrickSet().select_light(id)
|
brickset = BrickSet().select_light(id)
|
||||||
tag = BrickSetTagList.new().get(metadata_id)
|
tag = BrickSetTagList.get(metadata_id)
|
||||||
|
|
||||||
state = tag.update_set_state(brickset, json=request.json)
|
state = tag.update_set_state(brickset, json=request.json)
|
||||||
|
|
||||||
@ -127,9 +145,10 @@ def details(*, id: str) -> str:
|
|||||||
'set.html',
|
'set.html',
|
||||||
item=BrickSet().select_specific(id),
|
item=BrickSet().select_specific(id),
|
||||||
open_instructions=request.args.get('open_instructions'),
|
open_instructions=request.args.get('open_instructions'),
|
||||||
brickset_owners=BrickSetOwnerList.new().list(),
|
brickset_owners=BrickSetOwnerList.list(),
|
||||||
brickset_statuses=BrickSetStatusList.new().list(all=True),
|
brickset_statuses=BrickSetStatusList.list(all=True),
|
||||||
brickset_tags=BrickSetTagList.new().list(),
|
brickset_storages=BrickSetStorageList.list(as_class=True),
|
||||||
|
brickset_tags=BrickSetTagList.list(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ class BrickSetSocket extends BrickSocket {
|
|||||||
this.html_input = document.getElementById(`${id}-set`);
|
this.html_input = document.getElementById(`${id}-set`);
|
||||||
this.html_no_confim = document.getElementById(`${id}-no-confirm`);
|
this.html_no_confim = document.getElementById(`${id}-no-confirm`);
|
||||||
this.html_owners = document.getElementById(`${id}-owners`);
|
this.html_owners = document.getElementById(`${id}-owners`);
|
||||||
|
this.html_storage = document.getElementById(`${id}-storage`);
|
||||||
this.html_tags = document.getElementById(`${id}-tags`);
|
this.html_tags = document.getElementById(`${id}-tags`);
|
||||||
|
|
||||||
// Card elements
|
// Card elements
|
||||||
@ -151,6 +152,12 @@ class BrickSetSocket extends BrickSocket {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grab the storage
|
||||||
|
let storage = null;
|
||||||
|
if (this.html_storage) {
|
||||||
|
storage = this.html_storage.value;
|
||||||
|
}
|
||||||
|
|
||||||
// Grab the tags
|
// Grab the tags
|
||||||
const tags = [];
|
const tags = [];
|
||||||
if (this.html_tags) {
|
if (this.html_tags) {
|
||||||
@ -170,6 +177,7 @@ class BrickSetSocket extends BrickSocket {
|
|||||||
this.socket.emit(this.messages.IMPORT_SET, {
|
this.socket.emit(this.messages.IMPORT_SET, {
|
||||||
set: (set !== undefined) ? set : this.html_input.value,
|
set: (set !== undefined) ? set : this.html_input.value,
|
||||||
owners: owners,
|
owners: owners,
|
||||||
|
storage: storage,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
refresh: this.refresh
|
refresh: this.refresh
|
||||||
});
|
});
|
||||||
@ -285,6 +293,10 @@ class BrickSetSocket extends BrickSocket {
|
|||||||
this.html_owners.querySelectorAll('input').forEach(input => input.disabled = !enabled);
|
this.html_owners.querySelectorAll('input').forEach(input => input.disabled = !enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.html_storage) {
|
||||||
|
this.html_storage.disabled = !enabled;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.html_tags) {
|
if (this.html_tags) {
|
||||||
this.html_tags.querySelectorAll('input').forEach(input => input.disabled = !enabled);
|
this.html_tags.querySelectorAll('input').forEach(input => input.disabled = !enabled);
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,19 @@
|
|||||||
</div>
|
</div>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if brickset_storages | length %}
|
||||||
|
{{ accordion.header('Storage', 'storage', 'metadata', icon='archive-2-line') }}
|
||||||
|
<label class="visually-hidden" for="storage">{{ name }}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<select id="add-storage" class="form-select" autocomplete="off">
|
||||||
|
<option value="" selected><i>None</i></option>
|
||||||
|
{% for storage in brickset_storages %}
|
||||||
|
<option value="{{ storage.fields.id }}">{{ storage.fields.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{{ accordion.footer() }}
|
||||||
|
{% endif %}
|
||||||
{% if brickset_tags | length %}
|
{% if brickset_tags | length %}
|
||||||
{{ accordion.header('Tags', 'tags', 'metadata', icon='price-tag-2-line') }}
|
{{ accordion.header('Tags', 'tags', 'metadata', icon='price-tag-2-line') }}
|
||||||
<div id="add-tags">
|
<div id="add-tags">
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
{% include 'admin/owner/delete.html' %}
|
{% include 'admin/owner/delete.html' %}
|
||||||
{% elif delete_status %}
|
{% elif delete_status %}
|
||||||
{% include 'admin/status/delete.html' %}
|
{% include 'admin/status/delete.html' %}
|
||||||
|
{% elif delete_storage %}
|
||||||
|
{% include 'admin/storage/delete.html' %}
|
||||||
{% elif delete_tag %}
|
{% elif delete_tag %}
|
||||||
{% include 'admin/tag/delete.html' %}
|
{% include 'admin/tag/delete.html' %}
|
||||||
{% elif drop_database %}
|
{% elif drop_database %}
|
||||||
@ -36,6 +38,7 @@
|
|||||||
{% include 'admin/retired.html' %}
|
{% include 'admin/retired.html' %}
|
||||||
{% include 'admin/owner.html' %}
|
{% include 'admin/owner.html' %}
|
||||||
{% include 'admin/status.html' %}
|
{% include 'admin/status.html' %}
|
||||||
|
{% include 'admin/storage.html' %}
|
||||||
{% include 'admin/tag.html' %}
|
{% include 'admin/tag.html' %}
|
||||||
{% include 'admin/database.html' %}
|
{% include 'admin/database.html' %}
|
||||||
{% include 'admin/configuration.html' %}
|
{% include 'admin/configuration.html' %}
|
||||||
|
42
templates/admin/storage.html
Normal file
42
templates/admin/storage.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{% import 'macro/accordion.html' as accordion %}
|
||||||
|
|
||||||
|
{{ accordion.header('Set storages', 'storage', 'admin', expanded=open_storage, icon='archive-2-line', class='p-0') }}
|
||||||
|
{% if storage_error %}<div class="alert alert-danger m-2" role="alert"><strong>Error:</strong> {{ storage_error }}.</div>{% endif %}
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
{% if metadata_storages | length %}
|
||||||
|
{% for storage in metadata_storages %}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<form action="{{ url_for('admin_storage.rename', id=storage.fields.id) }}" method="post" class="row row-cols-lg-auto g-3 align-items-center">
|
||||||
|
<div class="col-12 flex-grow-1">
|
||||||
|
<label class="visually-hidden" for="name-{{ storage.fields.id }}">Name</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">Name</div>
|
||||||
|
<input type="text" class="form-control" id="name-{{ storage.fields.id }}" name="name" value="{{ storage.fields.name }}">
|
||||||
|
<button type="submit" class="btn btn-primary"><i class="ri-edit-line"></i> Rename</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<a href="{{ url_for('admin_storage.delete', id=storage.fields.id) }}" class="btn btn-danger" role="button"><i class="ri-delete-bin-2-line"></i> Delete</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<li class="list-group-item text-center"><i class="ri-error-warning-line"></i> No storage found.</li>
|
||||||
|
{% endif %}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<form action="{{ url_for('admin_storage.add') }}" method="post" class="row row-cols-lg-auto g-3 align-items-center">
|
||||||
|
<div class="col-12 flex-grow-1">
|
||||||
|
<label class="visually-hidden" for="name">Name</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">Name</div>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" value="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<button type="submit" class="btn btn-primary"><i class="ri-add-circle-line"></i> Add</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{{ accordion.footer() }}
|
19
templates/admin/storage/delete.html
Normal file
19
templates/admin/storage/delete.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{% import 'macro/accordion.html' as accordion %}
|
||||||
|
|
||||||
|
{{ accordion.header('Set storages danger zone', 'storage-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
||||||
|
<form action="{{ url_for('admin_storage.do_delete', id=storage.fields.id) }}" method="post">
|
||||||
|
{% if storage_error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ storage_error }}.</div>{% endif %}
|
||||||
|
<div class="alert alert-danger text-center" role="alert">You are about to <strong>delete a set storage</strong>. This action is irreversible.</div>
|
||||||
|
<div class="row row-cols-lg-auto g-3 align-items-center">
|
||||||
|
<div class="col-12 flex-grow-1">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-text">Name</div>
|
||||||
|
<input type="text" class="form-control" value="{{ storage.fields.name }}" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="border-bottom">
|
||||||
|
<a class="btn btn-danger" href="{{ url_for('admin.admin', open_storage=true) }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
||||||
|
<button type="submit" class="btn btn-danger"><i class="ri-delete-bin-2-line"></i> Delete <strong>the set storage</strong></button>
|
||||||
|
</form>
|
||||||
|
{{ accordion.footer() }}
|
@ -72,6 +72,18 @@
|
|||||||
{{ badge(check=set, url=url, solo=solo, last=last, color='secondary', icon='hashtag', collapsible='Set:', text=set, alt='Set') }}
|
{{ badge(check=set, url=url, solo=solo, last=last, color='secondary', icon='hashtag', collapsible='Set:', text=set, alt='Set') }}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro storage(item, storages, solo=false, last=false) %}
|
||||||
|
{% if item.fields.storage in storages.mapping %}
|
||||||
|
{% set storage = storages.mapping[item.fields.storage] %}
|
||||||
|
{% if last %}
|
||||||
|
{% set tooltip=storage.fields.name %}
|
||||||
|
{% else %}
|
||||||
|
{% set text=storage.fields.name %}
|
||||||
|
{% endif %}
|
||||||
|
{{ badge(check=storage, solo=solo, last=last, color='light text-warning-emphasis bg-warning-subtle border border-warning-subtle', icon='archive-2-line', text=text, alt='Storage', tooltip=tooltip) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro tag(item, tag, solo=false, last=false) %}
|
{% macro tag(item, tag, solo=false, last=false) %}
|
||||||
{% if last %}
|
{% if last %}
|
||||||
{% set tooltip=tag.fields.name %}
|
{% set tooltip=tag.fields.name %}
|
||||||
|
@ -39,3 +39,27 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro select(name, item, field, metadata_list, nullable=true, icon=none, delete=false) %}
|
||||||
|
{% if g.login.is_authenticated() %}
|
||||||
|
{% set prefix=metadata_list.as_prefix() %}
|
||||||
|
<label class="visually-hidden" for="{{ prefix }}-{{ item.fields.id }}">{{ name }}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
{% if icon %}<span class="input-group-text"><i class="ri-{{ icon }}"></i></span>{% endif %}
|
||||||
|
<select id="{{ prefix }}-{{ item.fields.id }}" class="form-select"
|
||||||
|
{% if not delete %}
|
||||||
|
data-changer-id="{{ item.fields.id }}" data-changer-prefix="{{ prefix }}" data-changer-url="{{ metadata_list.url_for_set_state(item.fields.id) }}"
|
||||||
|
{% else %}
|
||||||
|
disabled
|
||||||
|
{% endif %}
|
||||||
|
autocomplete="off">
|
||||||
|
{% if nullable %}<option value="" {% if item.fields[field] is none %}selected{% endif %}><i>None</i></option>{% endif %}
|
||||||
|
{% for metadata in metadata_list %}
|
||||||
|
<option value="{{ metadata.fields.id }}" {% if metadata.fields.id == item.fields[field] %}selected{% endif %}>{{ metadata.fields.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<span id="status-{{ prefix }}-{{ item.fields.id }}" class="input-group-text ri-save-line"></span>
|
||||||
|
<button id="clear-{{ prefix }}-{{ item.fields.id }}" type="button" class="btn btn-sm btn-light btn-outline-danger border"><i class="ri-eraser-line"></i></button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
@ -48,7 +48,8 @@
|
|||||||
{{ badge.total_damaged(item.fields.total_damaged, solo=solo, last=last) }}
|
{{ badge.total_damaged(item.fields.total_damaged, solo=solo, last=last) }}
|
||||||
{% for owner in brickset_owners %}
|
{% for owner in brickset_owners %}
|
||||||
{{ badge.owner(item, owner, solo=solo, last=last) }}
|
{{ badge.owner(item, owner, solo=solo, last=last) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{{ badge.storage(item, brickset_storages, solo=solo, last=last) }}
|
||||||
{% if not last %}
|
{% if not last %}
|
||||||
{% if not solo %}
|
{% if not solo %}
|
||||||
{{ badge.instructions(item, solo=solo, last=last) }}
|
{{ badge.instructions(item, solo=solo, last=last) }}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user