219 lines
6.3 KiB
Python
219 lines
6.3 KiB
Python
from sqlite3 import Row
|
|
from typing import Any, Self
|
|
|
|
from flask import current_app, url_for
|
|
|
|
from .exceptions import DatabaseException, NotFoundException
|
|
from .instructions import BrickInstructions
|
|
from .instructions_list import BrickInstructionsList
|
|
from .minifigure_list import BrickMinifigureList
|
|
from .part_list import BrickPartList
|
|
from .rebrickable_image import RebrickableImage
|
|
from .record import BrickRecord
|
|
from .sql import BrickSQL
|
|
from .theme_list import BrickThemeList
|
|
|
|
|
|
# Lego brick set
|
|
class BrickSet(BrickRecord):
|
|
instructions: list[BrickInstructions]
|
|
theme_name: str
|
|
|
|
# Queries
|
|
select_query: str = 'set/select'
|
|
insert_query: str = 'set/insert'
|
|
|
|
def __init__(
|
|
self,
|
|
/,
|
|
record: Row | dict[str, Any] | None = None,
|
|
):
|
|
super().__init__()
|
|
|
|
# Placeholders
|
|
self.theme_name = ''
|
|
self.instructions = []
|
|
|
|
# Ingest the record if it has one
|
|
if record is not None:
|
|
self.ingest(record)
|
|
|
|
# Resolve the theme
|
|
self.resolve_theme()
|
|
|
|
# Check for the instructions
|
|
self.resolve_instructions()
|
|
|
|
# Delete a set
|
|
def delete(self, /) -> None:
|
|
database = BrickSQL()
|
|
parameters = self.sql_parameters()
|
|
|
|
# Delete the set
|
|
database.execute('set/delete/set', parameters=parameters)
|
|
|
|
# Delete the minifigures
|
|
database.execute(
|
|
'minifigure/delete/all_from_set', parameters=parameters)
|
|
|
|
# Delete the parts
|
|
database.execute(
|
|
'part/delete/all_from_set', parameters=parameters)
|
|
|
|
# Delete missing parts
|
|
database.execute('missing/delete/all_from_set', parameters=parameters)
|
|
|
|
# Commit to the database
|
|
database.commit()
|
|
|
|
# Minifigures
|
|
def minifigures(self, /) -> BrickMinifigureList:
|
|
return BrickMinifigureList().load(self)
|
|
|
|
# Parts
|
|
def parts(self, /) -> BrickPartList:
|
|
return BrickPartList().load(self)
|
|
|
|
# Add instructions to the set
|
|
def resolve_instructions(self, /) -> None:
|
|
if self.fields.set_num is not None:
|
|
self.instructions = BrickInstructionsList().get(
|
|
self.fields.set_num
|
|
)
|
|
|
|
# Add a theme to the set
|
|
def resolve_theme(self, /) -> None:
|
|
try:
|
|
id = self.fields.theme_id
|
|
except Exception:
|
|
id = 0
|
|
|
|
theme = BrickThemeList().get(id)
|
|
self.theme_name = theme.name
|
|
|
|
# Return a short form of the set
|
|
def short(self, /) -> dict[str, Any]:
|
|
return {
|
|
'name': self.fields.name,
|
|
'set_img_url': self.fields.set_img_url,
|
|
'set_num': self.fields.set_num,
|
|
}
|
|
|
|
# Select a specific part (with a set and an id)
|
|
def select_specific(self, u_id: str, /) -> Self:
|
|
# Save the parameters to the fields
|
|
self.fields.u_id = u_id
|
|
|
|
# Load from database
|
|
record = self.select()
|
|
|
|
if record is None:
|
|
raise NotFoundException(
|
|
'Set with ID {id} was not found in the database'.format(
|
|
id=self.fields.u_id,
|
|
),
|
|
)
|
|
|
|
# Ingest the record
|
|
self.ingest(record)
|
|
|
|
# Resolve the theme
|
|
self.resolve_theme()
|
|
|
|
# Check for the instructions
|
|
self.resolve_instructions()
|
|
|
|
return self
|
|
|
|
# Update a checked state
|
|
def update_checked(self, name: str, status: bool, /) -> None:
|
|
parameters = self.sql_parameters()
|
|
parameters['status'] = status
|
|
|
|
# Update the checked status
|
|
rows, _ = BrickSQL().execute_and_commit(
|
|
'set/update_checked',
|
|
parameters=parameters,
|
|
name=name,
|
|
)
|
|
|
|
if rows != 1:
|
|
raise DatabaseException('Could not update the status {status} for set {number}'.format( # noqa: E501
|
|
status=name,
|
|
number=self.fields.set_num,
|
|
))
|
|
|
|
# Self url
|
|
def url(self, /) -> str:
|
|
return url_for('set.details', id=self.fields.u_id)
|
|
|
|
# Deletion url
|
|
def url_for_delete(self, /) -> str:
|
|
return url_for('set.delete', id=self.fields.u_id)
|
|
|
|
# Actual deletion url
|
|
def url_for_do_delete(self, /) -> str:
|
|
return url_for('set.do_delete', id=self.fields.u_id)
|
|
|
|
# Compute the url for the set image
|
|
def url_for_image(self, /) -> str:
|
|
if not current_app.config['USE_REMOTE_IMAGES'].value:
|
|
return RebrickableImage.static_url(
|
|
self.fields.set_num,
|
|
'SETS_FOLDER'
|
|
)
|
|
else:
|
|
return self.fields.set_img_url
|
|
|
|
# Compute the url for the set instructions
|
|
def url_for_instructions(self, /) -> str:
|
|
if len(self.instructions):
|
|
return url_for(
|
|
'set.details',
|
|
id=self.fields.u_id,
|
|
open_instructions=True
|
|
)
|
|
else:
|
|
return ''
|
|
|
|
# Check minifigure collected url
|
|
def url_for_minifigures_collected(self, /) -> str:
|
|
return url_for('set.minifigures_collected', id=self.fields.u_id)
|
|
|
|
# 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_SET_PATTERN'].value.format( # noqa: E501
|
|
number=self.fields.set_num.lower(),
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
return ''
|
|
|
|
# Check set checked url
|
|
def url_for_set_checked(self, /) -> str:
|
|
return url_for('set.set_checked', id=self.fields.u_id)
|
|
|
|
# Check set collected url
|
|
def url_for_set_collected(self, /) -> str:
|
|
return url_for('set.set_collected', id=self.fields.u_id)
|
|
|
|
# Normalize from Rebrickable
|
|
@staticmethod
|
|
def from_rebrickable(data: dict[str, Any], /, **_) -> dict[str, Any]:
|
|
return {
|
|
'set_num': data['set_num'],
|
|
'name': data['name'],
|
|
'year': data['year'],
|
|
'theme_id': data['theme_id'],
|
|
'num_parts': data['num_parts'],
|
|
'set_img_url': data['set_img_url'],
|
|
'set_url': data['set_url'],
|
|
'last_modified_dt': data['last_modified_dt'],
|
|
'mini_col': False,
|
|
'set_col': False,
|
|
'set_check': False,
|
|
}
|