BrickTracker/bricktracker/part.py

197 lines
5.8 KiB
Python
Raw Normal View History

2025-01-28 19:18:51 +01:00
import logging
2025-01-17 11:03:00 +01:00
from typing import Any, Self, TYPE_CHECKING
2025-01-28 19:18:51 +01:00
import traceback
2025-01-17 11:03:00 +01:00
from flask import url_for
2025-01-28 19:18:51 +01:00
from .exceptions import ErrorException, NotFoundException
from .rebrickable_part import RebrickablePart
2025-01-17 11:03:00 +01:00
from .sql import BrickSQL
if TYPE_CHECKING:
from .minifigure import BrickMinifigure
from .set import BrickSet
2025-01-28 19:18:51 +01:00
from .socket import BrickSocket
logger = logging.getLogger(__name__)
2025-01-17 11:03:00 +01:00
# Lego set or minifig part
2025-01-28 19:18:51 +01:00
class BrickPart(RebrickablePart):
identifier: str
kind: str
2025-01-17 11:03:00 +01:00
# Queries
insert_query: str = 'part/insert'
generic_query: str = 'part/select/generic'
select_query: str = 'part/select/specific'
2025-01-28 19:18:51 +01:00
def __init__(self, /, **kwargs):
super().__init__(**kwargs)
2025-01-17 11:03:00 +01:00
2025-01-28 19:18:51 +01:00
if self.minifigure is not None:
self.identifier = self.minifigure.fields.figure
self.kind = 'Minifigure'
elif self.brickset is not None:
self.identifier = self.brickset.fields.set
self.kind = 'Set'
# Import a part into the database
2025-01-28 23:07:12 +01:00
def download(self, socket: 'BrickSocket', refresh: bool = False) -> bool:
2025-01-28 19:18:51 +01:00
if self.brickset is None:
raise ErrorException('Importing a part from Rebrickable outside of a set is not supported') # noqa: E501
try:
# Insert into the database
socket.auto_progress(
message='{kind} {identifier}: inserting part {part} into database'.format( # noqa: E501
kind=self.kind,
identifier=self.identifier,
part=self.fields.part
)
)
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-28 19:18:51 +01:00
# Insert the rebrickable set into database
self.insert_rebrickable()
2025-01-17 11:03:00 +01:00
2025-01-28 19:18:51 +01:00
except Exception as e:
socket.fail(
message='Error while importing part {part} from {kind} {identifier}: {error}'.format( # noqa: E501
part=self.fields.part,
kind=self.kind,
identifier=self.identifier,
error=e,
)
)
2025-01-17 11:03:00 +01:00
2025-01-28 19:18:51 +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
2025-01-17 11:03:00 +01:00
2025-01-28 19:18:51 +01:00
return True
# A identifier for HTML component
2025-01-31 20:46:36 +01:00
def html_id(self, prefix: str | None = None, /) -> str:
components: list[str] = ['part']
2025-01-17 11:03:00 +01:00
2025-01-31 20:46:36 +01:00
if prefix is not None:
components.append(prefix)
2025-01-28 19:18:51 +01:00
if self.fields.figure is not None:
components.append(self.fields.figure)
components.append(self.fields.part)
components.append(str(self.fields.color))
components.append(str(self.fields.spare))
return '-'.join(components)
2025-01-17 11:03:00 +01:00
# Select a generic part
def select_generic(
self,
2025-01-28 19:18:51 +01:00
part: str,
color: int,
2025-01-17 11:03:00 +01:00
/,
) -> Self:
# Save the parameters to the fields
2025-01-28 19:18:51 +01:00
self.fields.part = part
self.fields.color = color
2025-01-17 11:03:00 +01:00
if not self.select(override_query=self.generic_query):
2025-01-17 11:03:00 +01:00
raise NotFoundException(
2025-01-28 19:18:51 +01:00
'Part with number {number}, color ID {color} was not found in the database'.format( # noqa: E501
number=self.fields.part,
color=self.fields.color,
2025-01-17 11:03:00 +01:00
),
)
return self
# Select a specific part (with a set and an id, and option. a minifigure)
def select_specific(
self,
brickset: 'BrickSet',
2025-01-28 19:18:51 +01:00
part: str,
color: int,
spare: int,
2025-01-17 11:03:00 +01:00
/,
*,
2025-01-17 11:03:00 +01:00
minifigure: 'BrickMinifigure | None' = None,
) -> Self:
# Save the parameters to the fields
self.brickset = brickset
self.minifigure = minifigure
2025-01-28 19:18:51 +01:00
self.fields.part = part
self.fields.color = color
self.fields.spare = spare
2025-01-17 11:03:00 +01:00
if not self.select():
2025-01-28 19:18:51 +01:00
if self.minifigure is not None:
figure = self.minifigure.fields.figure
else:
figure = None
2025-01-17 11:03:00 +01:00
raise NotFoundException(
2025-01-28 19:18:51 +01:00
'Part {part} with color {color} (spare: {spare}) from set {set} ({id}) (minifigure: {figure}) was not found in the database'.format( # noqa: E501
part=self.fields.part,
color=self.fields.color,
spare=self.fields.spare,
2025-01-17 11:03:00 +01:00
id=self.fields.id,
set=self.brickset.fields.set,
2025-01-28 19:18:51 +01:00
figure=figure,
2025-01-17 11:03:00 +01:00
),
)
return self
2025-01-31 20:46:36 +01:00
# Update a problematic part
def update_problem(self, problem: str, json: Any | None, /) -> int:
amount: str | int = json.get('value', '') # type: ignore
2025-01-29 15:58:41 +01:00
2025-01-28 19:18:51 +01:00
# We need a positive integer
try:
2025-01-31 20:46:36 +01:00
if amount == '':
amount = 0
2025-01-31 20:46:36 +01:00
amount = int(amount)
2025-01-17 11:03:00 +01:00
2025-01-31 20:46:36 +01:00
if amount < 0:
amount = 0
2025-01-28 19:18:51 +01:00
except Exception:
2025-01-31 20:46:36 +01:00
raise ErrorException('"{amount}" is not a valid integer'.format(
amount=amount
2025-01-28 19:18:51 +01:00
))
2025-01-17 11:03:00 +01:00
2025-01-31 20:46:36 +01:00
if amount < 0:
raise ErrorException('Cannot set a negative amount')
2025-01-17 11:03:00 +01:00
2025-01-31 20:46:36 +01:00
setattr(self.fields, problem, amount)
2025-01-17 11:03:00 +01:00
2025-01-28 19:18:51 +01:00
BrickSQL().execute_and_commit(
2025-01-31 20:46:36 +01:00
'part/update/{problem}'.format(problem=problem),
2025-01-28 19:18:51 +01:00
parameters=self.sql_parameters()
2025-01-17 11:03:00 +01:00
)
2025-01-31 20:46:36 +01:00
return amount
# Compute the url for problematic part
def url_for_problem(self, problem: str, /) -> str:
# Different URL for a minifigure part
if self.minifigure is not None:
figure = self.minifigure.fields.figure
else:
figure = None
return url_for(
2025-01-31 20:46:36 +01:00
'set.problem_part',
id=self.fields.id,
figure=figure,
part=self.fields.part,
color=self.fields.color,
spare=self.fields.spare,
2025-01-31 20:46:36 +01:00
problem=problem,
)