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