2025-01-24 10:36:24 +01:00
|
|
|
import logging
|
|
|
|
import traceback
|
2025-01-27 14:20:12 +01:00
|
|
|
from typing import Any, Self, TYPE_CHECKING
|
2025-01-24 10:36:24 +01:00
|
|
|
from uuid import uuid4
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-26 09:59:53 +01:00
|
|
|
from flask import current_app, url_for
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-30 12:17:25 +01:00
|
|
|
from .exceptions import NotFoundException
|
2025-01-17 11:03:00 +01:00
|
|
|
from .minifigure_list import BrickMinifigureList
|
|
|
|
from .part_list import BrickPartList
|
2025-01-24 10:36:24 +01:00
|
|
|
from .rebrickable_set import RebrickableSet
|
2025-01-31 16:34:52 +01:00
|
|
|
from .set_owner import BrickSetOwner
|
|
|
|
from .set_owner_list import BrickSetOwnerList
|
2025-01-30 16:23:47 +01:00
|
|
|
from .set_status import BrickSetStatus
|
2025-01-30 15:03:16 +01:00
|
|
|
from .set_status_list import BrickSetStatusList
|
2025-01-31 18:08:53 +01:00
|
|
|
from .set_tag import BrickSetTag
|
|
|
|
from .set_tag_list import BrickSetTagList
|
2025-01-17 11:03:00 +01:00
|
|
|
from .sql import BrickSQL
|
2025-01-27 14:20:12 +01:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from .socket import BrickSocket
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
logger = logging.getLogger(__name__)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
# Lego brick set
|
|
|
|
class BrickSet(RebrickableSet):
|
2025-01-17 11:03:00 +01:00
|
|
|
# Queries
|
2025-01-24 10:36:24 +01:00
|
|
|
select_query: str = 'set/select/full'
|
|
|
|
light_query: str = 'set/select/light'
|
2025-01-17 11:03:00 +01:00
|
|
|
insert_query: str = 'set/insert'
|
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
# Delete a set
|
|
|
|
def delete(self, /) -> None:
|
|
|
|
BrickSQL().executescript(
|
|
|
|
'set/delete/set',
|
|
|
|
id=self.fields.id
|
|
|
|
)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
# Import a set into the database
|
2025-01-28 19:18:51 +01:00
|
|
|
def download(self, socket: 'BrickSocket', data: dict[str, Any], /) -> bool:
|
2025-01-24 10:36:24 +01:00
|
|
|
# Load the set
|
2025-01-27 14:20:12 +01:00
|
|
|
if not self.load(socket, data, from_download=True):
|
2025-01-28 19:18:51 +01:00
|
|
|
return False
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
try:
|
|
|
|
# Insert into the database
|
2025-01-27 14:20:12 +01:00
|
|
|
socket.auto_progress(
|
2025-01-27 12:04:20 +01:00
|
|
|
message='Set {set}: inserting into database'.format(
|
|
|
|
set=self.fields.set
|
2025-01-24 10:36:24 +01:00
|
|
|
),
|
|
|
|
increment_total=True,
|
|
|
|
)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-28 23:07:12 +01:00
|
|
|
# Grabbing the refresh flag
|
|
|
|
refresh: bool = bool(data.get('refresh', False))
|
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
# Generate an UUID for self
|
|
|
|
self.fields.id = str(uuid4())
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-28 23:07:12 +01:00
|
|
|
if not refresh:
|
|
|
|
# Insert into database
|
|
|
|
self.insert(commit=False)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-27 18:39:35 +01:00
|
|
|
# Insert the rebrickable set into database
|
2025-01-27 10:04:24 +01:00
|
|
|
self.insert_rebrickable()
|
2025-01-24 10:36:24 +01:00
|
|
|
|
|
|
|
# Load the inventory
|
2025-01-28 23:07:12 +01:00
|
|
|
if not BrickPartList.download(socket, self, refresh=refresh):
|
2025-01-28 19:18:51 +01:00
|
|
|
return False
|
2025-01-24 10:36:24 +01:00
|
|
|
|
|
|
|
# Load the minifigures
|
2025-01-28 23:07:12 +01:00
|
|
|
if not BrickMinifigureList.download(socket, self, refresh=refresh):
|
2025-01-28 19:18:51 +01:00
|
|
|
return False
|
2025-01-24 10:36:24 +01:00
|
|
|
|
2025-01-31 16:34:52 +01:00
|
|
|
# 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)
|
|
|
|
|
2025-01-31 18:08:53 +01:00
|
|
|
# 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)
|
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
# Commit the transaction to the database
|
2025-01-27 14:20:12 +01:00
|
|
|
socket.auto_progress(
|
2025-01-27 12:04:20 +01:00
|
|
|
message='Set {set}: writing to the database'.format(
|
|
|
|
set=self.fields.set
|
2025-01-24 10:36:24 +01:00
|
|
|
),
|
|
|
|
increment_total=True,
|
|
|
|
)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
BrickSQL().commit()
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-28 23:07:12 +01:00
|
|
|
if refresh:
|
|
|
|
# Info
|
|
|
|
logger.info('Set {set}: imported (id: {id})'.format(
|
2025-01-27 12:04:20 +01:00
|
|
|
set=self.fields.set,
|
2025-01-28 23:07:12 +01:00
|
|
|
id=self.fields.id,
|
|
|
|
))
|
|
|
|
|
|
|
|
# Complete
|
|
|
|
socket.complete(
|
|
|
|
message='Set {set}: refreshed'.format( # noqa: E501
|
|
|
|
set=self.fields.set,
|
|
|
|
),
|
|
|
|
download=True
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
# Info
|
|
|
|
logger.info('Set {set}: refreshed'.format(
|
|
|
|
set=self.fields.set,
|
|
|
|
))
|
|
|
|
|
|
|
|
# Complete
|
|
|
|
socket.complete(
|
|
|
|
message='Set {set}: imported (<a href="{url}">Go to the set</a>)'.format( # noqa: E501
|
|
|
|
set=self.fields.set,
|
|
|
|
url=self.url()
|
|
|
|
),
|
|
|
|
download=True
|
|
|
|
)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
except Exception as e:
|
2025-01-27 14:20:12 +01:00
|
|
|
socket.fail(
|
2025-01-27 12:04:20 +01:00
|
|
|
message='Error while importing set {set}: {error}'.format(
|
|
|
|
set=self.fields.set,
|
2025-01-24 10:36:24 +01:00
|
|
|
error=e,
|
|
|
|
)
|
|
|
|
)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
logger.debug(traceback.format_exc())
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-28 19:18:51 +01:00
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2025-01-17 11:03:00 +01:00
|
|
|
# Minifigures
|
|
|
|
def minifigures(self, /) -> BrickMinifigureList:
|
2025-01-27 17:07:30 +01:00
|
|
|
return BrickMinifigureList().from_set(self)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
|
|
|
# Parts
|
|
|
|
def parts(self, /) -> BrickPartList:
|
2025-01-28 19:18:51 +01:00
|
|
|
return BrickPartList().list_specific(self)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
# Select a light set (with an id)
|
|
|
|
def select_light(self, id: str, /) -> Self:
|
2025-01-17 11:03:00 +01:00
|
|
|
# Save the parameters to the fields
|
2025-01-24 10:36:24 +01:00
|
|
|
self.fields.id = id
|
2025-01-17 11:03:00 +01:00
|
|
|
|
|
|
|
# Load from database
|
2025-01-24 10:36:24 +01:00
|
|
|
if not self.select(override_query=self.light_query):
|
2025-01-17 11:03:00 +01:00
|
|
|
raise NotFoundException(
|
|
|
|
'Set with ID {id} was not found in the database'.format(
|
2025-01-24 10:36:24 +01:00
|
|
|
id=self.fields.id,
|
2025-01-17 11:03:00 +01:00
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
return self
|
2025-01-17 11:03:00 +01:00
|
|
|
|
2025-01-24 10:36:24 +01:00
|
|
|
# Select a specific set (with an id)
|
|
|
|
def select_specific(self, id: str, /) -> Self:
|
|
|
|
# Save the parameters to the fields
|
|
|
|
self.fields.id = id
|
|
|
|
|
|
|
|
# Load from database
|
|
|
|
if not self.select(
|
2025-01-31 16:34:52 +01:00
|
|
|
owners=BrickSetOwnerList(BrickSetOwner).as_columns(),
|
2025-01-31 18:08:53 +01:00
|
|
|
statuses=BrickSetStatusList(BrickSetStatus).as_columns(all=True),
|
|
|
|
tags=BrickSetTagList(BrickSetTag).as_columns(),
|
2025-01-24 10:36:24 +01:00
|
|
|
):
|
|
|
|
raise NotFoundException(
|
|
|
|
'Set with ID {id} was not found in the database'.format(
|
|
|
|
id=self.fields.id,
|
|
|
|
),
|
|
|
|
)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
# Self url
|
|
|
|
def url(self, /) -> str:
|
2025-01-24 10:36:24 +01:00
|
|
|
return url_for('set.details', id=self.fields.id)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
|
|
|
# Deletion url
|
|
|
|
def url_for_delete(self, /) -> str:
|
2025-01-24 10:36:24 +01:00
|
|
|
return url_for('set.delete', id=self.fields.id)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
|
|
|
# Actual deletion url
|
|
|
|
def url_for_do_delete(self, /) -> str:
|
2025-01-24 10:36:24 +01:00
|
|
|
return url_for('set.do_delete', id=self.fields.id)
|
2025-01-17 11:03:00 +01:00
|
|
|
|
|
|
|
# Compute the url for the set instructions
|
|
|
|
def url_for_instructions(self, /) -> str:
|
2025-01-26 09:59:53 +01:00
|
|
|
if (
|
|
|
|
not current_app.config['HIDE_SET_INSTRUCTIONS'] and
|
|
|
|
len(self.instructions)
|
|
|
|
):
|
2025-01-17 11:03:00 +01:00
|
|
|
return url_for(
|
|
|
|
'set.details',
|
2025-01-24 10:36:24 +01:00
|
|
|
id=self.fields.id,
|
2025-01-17 11:03:00 +01:00
|
|
|
open_instructions=True
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
return ''
|
2025-01-28 23:07:12 +01:00
|
|
|
|
|
|
|
# Compute the url for the refresh button
|
|
|
|
def url_for_refresh(self, /) -> str:
|
|
|
|
return url_for(
|
|
|
|
'set.refresh',
|
|
|
|
id=self.fields.id,
|
|
|
|
)
|