BrickTracker/bricktracker/minifigure.py
2025-01-17 11:03:00 +01:00

184 lines
5.4 KiB
Python

import os
from sqlite3 import Row
from typing import Any, Self, TYPE_CHECKING
from flask import current_app, url_for
from .exceptions import ErrorException, NotFoundException
from .part_list import BrickPartList
from .rebrickable_image import RebrickableImage
from .record import BrickRecord
if TYPE_CHECKING:
from .set import BrickSet
# Lego minifigure
class BrickMinifigure(BrickRecord):
brickset: 'BrickSet | None'
# Queries
insert_query: str = 'minifigure/insert'
generic_query: str = 'minifigure/select/generic'
select_query: str = 'minifigure/select/specific'
def __init__(
self,
/,
brickset: 'BrickSet | None' = None,
record: Row | dict[str, Any] | None = None,
):
super().__init__()
# Save the brickset
self.brickset = brickset
# Ingest the record if it has one
if record is not None:
self.ingest(record)
# Return the number just in digits format
def clean_number(self, /) -> str:
number: str = self.fields.fig_num
number = number.removeprefix('fig-')
number = number.lstrip('0')
return number
# Parts
def generic_parts(self, /) -> BrickPartList:
return BrickPartList().from_minifigure(self)
# Parts
def parts(self, /) -> BrickPartList:
if self.brickset is None:
raise ErrorException('Part list for minifigure {number} requires a brickset'.format( # noqa: E501
number=self.fields.fig_num,
))
return BrickPartList().load(self.brickset, minifigure=self)
# Select a generic minifigure
def select_generic(self, fig_num: str, /) -> Self:
# Save the parameters to the fields
self.fields.fig_num = fig_num
record = self.select(override_query=self.generic_query)
if record is None:
raise NotFoundException(
'Minifigure with number {number} was not found in the database'.format( # noqa: E501
number=self.fields.fig_num,
),
)
# Ingest the record
self.ingest(record)
return self
# Select a specific minifigure (with a set and an number)
def select_specific(self, brickset: 'BrickSet', fig_num: str, /) -> Self:
# Save the parameters to the fields
self.brickset = brickset
self.fields.fig_num = fig_num
record = self.select()
if record is None:
raise NotFoundException(
'Minifigure with number {number} from set {set} was not found in the database'.format( # noqa: E501
number=self.fields.fig_num,
set=self.brickset.fields.set_num,
),
)
# Ingest the record
self.ingest(record)
return self
# Return a dict with common SQL parameters for a minifigure
def sql_parameters(self, /) -> dict[str, Any]:
parameters = super().sql_parameters()
# Supplement from the brickset
if self.brickset is not None:
if 'u_id' not in parameters:
parameters['u_id'] = self.brickset.fields.u_id
if 'set_num' not in parameters:
parameters['set_num'] = self.brickset.fields.set_num
return parameters
# Self url
def url(self, /) -> str:
return url_for(
'minifigure.details',
number=self.fields.fig_num,
)
# Compute the url for minifigure part image
def url_for_image(self, /) -> str:
if not current_app.config['USE_REMOTE_IMAGES'].value:
if self.fields.set_img_url is None:
file = RebrickableImage.nil_minifigure_name()
else:
file = self.fields.fig_num
folder: str = current_app.config['MINIFIGURES_FOLDER'].value
# /!\ Everything is saved as .jpg, even if it came from a .png
# not changing this behaviour.
# Grab the extension
# _, extension = os.path.splitext(self.part_img_url)
extension = '.jpg'
# Compute the path
path = os.path.join(folder, '{number}{ext}'.format(
number=file,
ext=extension,
))
return url_for('static', filename=path)
else:
if self.fields.set_img_url is None:
return current_app.config['REBRICKABLE_IMAGE_NIL_MINIFIGURE'].value # noqa: E501
else:
return self.fields.set_img_url
# Compute the url for the rebrickable page
def url_for_rebrickable(self, /) -> str:
if current_app.config['REBRICKABLE_LINKS'].value:
try:
return current_app.config['REBRICKABLE_LINK_MINIFIGURE_PATTERN'].value.format( # noqa: E501
number=self.fields.fig_num.lower(),
)
except Exception:
pass
return ''
# Normalize from Rebrickable
@staticmethod
def from_rebrickable(
data: dict[str, Any],
/,
brickset: 'BrickSet | None' = None,
**_,
) -> dict[str, Any]:
record = {
'fig_num': data['set_num'],
'name': data['set_name'],
'quantity': data['quantity'],
'set_img_url': data['set_img_url'],
}
if brickset is not None:
record['set_num'] = brickset.fields.set_num
record['u_id'] = brickset.fields.u_id
return record