from typing import Callable, Optional, Self
NAME_ENV_DIRNAME = 'BRICKSPLOM_DIR'
-PATH_COLLECTIONS = 'collections.txt'
+PATH_SETS = 'sets.txt'
PATH_COLORS = 'colors.txt'
PATH_DESIGNS = 'designs.txt'
-PATH_PIECES = 'pieces.txt'
+PATH_BRICKS = 'bricks.txt'
CHAR_NEWLINE = '\n'
CHAR_SEP_TOKEN = ' '
CHAR_Q_TEXT = '?'
BOX_PREFIX = 'box:'
-PieceListing = tuple[int, str, str]
-PageColumn = tuple[PieceListing, ...]
+BrickListing = tuple[int, str, str]
+PageColumn = tuple[BrickListing, ...]
Page = tuple[PageColumn, ...]
return self.matchers[query_char](query_body)
-class Color(Textfiled, Lookupable):
+class BrickColor(Textfiled, Lookupable):
'Color incl. solidness/transparency field.'
_id_indent = 3
+ self.wavelength)
-class Design(Textfiled, Lookupable):
+class BrickDesign(Textfiled, Lookupable):
'Shape and texture configurations with descriptions and equalities.'
_id_indent = 6
alternate_to: Optional[Self] = None
+ f' {self._description}')))
-class Piece(Textfiled, WithDb, Lookupable):
+class Brick(Textfiled, WithDb, Lookupable):
'Individual configuration of design and color.'
_id_indent = 7
) -> dict[str, Self]:
collected = {}
for toks in [cls.tokify(line, 3) for line in cls.lines_of(path)]:
- piece_id, design_id = toks[:2]
- assert piece_id not in collected
+ brick_id, design_id = toks[:2]
+ assert brick_id not in collected
color_id, comment = (toks[-1].split(CHAR_SEP_TOKEN, maxsplit=1)
+ [''])[:2]
- collected[piece_id] = cls(piece_id, design_id, color_id, comment,
+ collected[brick_id] = cls(brick_id, design_id, color_id, comment,
db=db)
return collected
def raw(
self
) -> str:
- return (f'{self.id_indented()} {Design.indent_id(self.design_id)} '
- f'{Color.indent_id(self.color_id)} {self.comment}').rstrip()
+ return (f'{self.id_indented()} '
+ f'{BrickDesign.indent_id(self.design_id)} '
+ f'{BrickColor.indent_id(self.color_id)} {self.comment}'
+ ).rstrip()
def __str__(
self
design = self._db.designs[self.design_id]
color = str(self._db.colors[self.color_id]).strip()
comment = f' # {self.comment}' if self.comment else ''
- return (f'{self.id_indented()} {Design.indent_id(self.design_id)} '
+ return (f'{self.id_indented()} '
+ f'{BrickDesign.indent_id(self.design_id)} '
f'{design.description} ({color}){comment}')
-class Collection(Textfiled, WithDb, Lookupable):
- 'Named collection of pieces in order of pages of columns of counts.'
+class BrickSet(Textfiled, WithDb, Lookupable):
+ 'Named collection of bricks in order of pages of columns of counts.'
def __init__(
self,
id_: str,
is_in: Optional[bool],
description: str,
- piece_listings: tuple[Page, ...],
+ brick_listings: tuple[Page, ...],
**kwargs
) -> None:
self.id_ = id_
self.is_in = is_in
self.description = description
- self.piece_listings = piece_listings
+ self.brick_listings = brick_listings
super().__init__(**kwargs)
@classmethod
) -> dict[str, Self]:
collected: dict[str, tuple[Optional[bool],
str,
- list[list[list[PieceListing]]]]]
+ list[list[list[BrickListing]]]]]
collected = {}
- i_listings: list[list[list[PieceListing]]] = [[[]]]
+ i_listings: list[list[list[BrickListing]]] = [[[]]]
for line in cls.lines_of(path):
if not line.startswith(CHAR_COLL_INDENT):
id_, metadata = cls.tokify(line, 2)
k: cls(id_=k,
is_in=v[0],
description=v[1],
- piece_listings=tuple(tuple(tuple(column) for column in page)
+ brick_listings=tuple(tuple(tuple(column) for column in page)
for page in v[2]),
db=db)
for k, v in collected.items()}
return (f'{self.id_indented()} '
f'{self._is_in_str}{self.description}{CHAR_NEWLINE}'
+ self._format_paginated(lambda count, p_id, comment:
- f' {count:2} {Piece.indent_id(p_id)}'
+ f' {count:2} {Brick.indent_id(p_id)}'
+ (f' {comment}' if comment else '')))
def __str__(
format_line: Callable[[int, str, str], str]
) -> str:
lines = []
- for idx_pages, page in enumerate(self.piece_listings):
+ for idx_pages, page in enumerate(self.brick_listings):
if idx_pages != 0:
lines += [' =']
for idx_columns, column in enumerate(page):
if idx_columns != 0:
lines += [' -']
- for count, piece_id, comment in column:
- lines += [format_line(count, piece_id, comment)]
+ for count, brick_id, comment in column:
+ lines += [format_line(count, brick_id, comment)]
return CHAR_NEWLINE.join(lines) + CHAR_NEWLINE
- def piece_listings_flat(self) -> tuple[PieceListing, ...]:
- 'Flattened variant of .piece_listings, no division into pages/cols.'
- collected: list[PieceListing] = []
- for page in self.piece_listings:
+ def brick_listings_flat(self) -> tuple[BrickListing, ...]:
+ 'Flattened variant of .brick_listings, no division into pages/cols.'
+ collected: list[BrickListing] = []
+ for page in self.brick_listings:
for column in page:
collected += list(column)
return tuple(collected)
def show(
self
) -> str:
- def format_line(count, piece_id, comment) -> str:
+ def format_line(count, brick_id, comment) -> str:
assert self._db
- piece = self._db.pieces[piece_id]
- design_id = piece.design_id
+ brick = self._db.bricks[brick_id]
+ design_id = brick.design_id
tail_comment = f' # {comment}' if comment else ''
- color = str(self._db.colors[self._db.pieces[piece_id].color_id]
+ color = str(self._db.colors[self._db.bricks[brick_id].color_id]
).lstrip().split(CHAR_SEP_TOKEN, maxsplit=1)[1]
box: Optional[Box] = None
for i_box in self._db.boxes().values():
for idx_in_box in [
idx for idx, d_to_ls
in enumerate(i_box.designs_to_listings)
- if piece_id in [listing[1] for listing in d_to_ls[1]]]:
+ if brick_id in [listing[1] for listing in d_to_ls[1]]]:
box = i_box
break
if box:
break
box_listing = ((box.id_indented() if box else Box.indent_id(''))
+ ':' + (f'{idx_in_box:>2}' if box else '__'))
- return (f'{count:>2}× {Piece.indent_id(piece_id)}:'
- f'{Design.indent_id(design_id)} {box_listing} {color} '
- f'{self._db.designs[design_id].description}{tail_comment}')
+ return (
+ f'{count:>2}× {Brick.indent_id(brick_id)}:'
+ f'{BrickDesign.indent_id(design_id)} {box_listing} {color} '
+ f'{self._db.designs[design_id].description}{tail_comment}')
return f'{self}{CHAR_NEWLINE}{self._format_paginated(format_line)}'
def __init__(
self,
id_: str,
- collection: Collection,
+ bricks_set: BrickSet,
**kwargs
) -> None:
super().__init__(**kwargs)
assert self._db
self.id_ = id_
- self._collection = collection
- designed_listings: list[tuple[Design, list[PieceListing]]] = []
- for listing in self._collection.piece_listings_flat():
- design = self._db.designs[self._db.pieces[listing[1]].design_id]
+ self._set = bricks_set
+ designed_listings: list[tuple[BrickDesign, list[BrickListing]]] = []
+ for listing in self._set.brick_listings_flat():
+ design = self._db.designs[self._db.bricks[listing[1]].design_id]
design = design.alternate_to or design
if not (designed_listings and designed_listings[-1][0] == design):
designed_listings += [(design, []), ]
for design, listings in self.designs_to_listings:
lines += [
f'=== {" / ".join(design.all_ids)}: {design.description} ===']
- for count, piece_id, comment in listings:
- color = self._db.colors[self._db.pieces[piece_id].color_id]
- lines += [f'{count:>2}× {Piece.indent_id(piece_id)} / {color} '
+ for count, brick_id, comment in listings:
+ color = self._db.colors[self._db.bricks[brick_id].color_id]
+ lines += [f'{count:>2}× {Brick.indent_id(brick_id)} / {color} '
f'# {comment}']
return CHAR_NEWLINE.join(lines)
def raw(
self
) -> str:
- 'Call .raw() of base Collection.'
- return self._collection.raw()
+ 'Call .raw() of base BrickSet.'
+ return self._set.raw()
class BricksDb:
'Collection of all the tables enabling their combined processing.'
lookupable = frozenset(
- {'boxes', 'designs', 'collections', 'colors', 'pieces'})
+ {'boxes', 'designs', 'sets', 'colors', 'bricks'})
def __init__(
self,
path_tables: str
) -> None:
- self.colors = Color.from_textfile((path_tables, PATH_COLORS))
- self.pieces = Piece.from_textfile((path_tables, PATH_PIECES), db=self)
- self.collections = Collection.from_textfile(
- (path_tables, PATH_COLLECTIONS), db=self)
- self.designs = Design.from_textfile((path_tables, PATH_DESIGNS))
+ self.colors = BrickColor.from_textfile((path_tables, PATH_COLORS))
+ self.bricks = Brick.from_textfile((path_tables, PATH_BRICKS), db=self)
+ self.sets = BrickSet.from_textfile((path_tables, PATH_SETS), db=self)
+ self.designs = BrickDesign.from_textfile((path_tables, PATH_DESIGNS))
self._check_consistencies_between_tables()
def boxes(
self
) -> dict[str, Box]:
- 'Parsed from BOX_PREFIX-prefixed entries in .collections.'
+ 'Parsed from BOX_PREFIX-prefixed entries in .sets.'
collected = {}
- for collection in [c for c in self.collections.values()
+ for bricks_set in [c for c in self.sets.values()
if c.id_.startswith(BOX_PREFIX)]:
- box_id = collection.id_[len(BOX_PREFIX):]
- collected[box_id] = Box(box_id, collection, db=self)
+ box_id = bricks_set.id_[len(BOX_PREFIX):]
+ collected[box_id] = Box(box_id, bricks_set, db=self)
return collected
def _check_consistencies_between_tables(
) -> None:
fails = []
- # check all items listed in collections recorded in pieces
- for coll in self.collections.values():
- for piece_id in [t[1] for t in coll.piece_listings_flat()
- if t[1] not in self.pieces]:
- fails += [f'missing Piece of ID: {piece_id}']
+ # check all items listed in sets recorded in bricks
+ for coll in self.sets.values():
+ for brick_id in [t[1] for t in coll.brick_listings_flat()
+ if t[1] not in self.bricks]:
+ fails += [f'missing brick of ID: {brick_id}']
- # check all pieces' designs recorded in designs
- for d_id in [p.design_id for p in self.pieces.values()
+ # check all bricks' designs recorded in designs
+ for d_id in [p.design_id for p in self.bricks.values()
if p.design_id not in self.designs]:
- fails += [f'missing Design of ID: {d_id}']
+ fails += [f'missing design of ID: {d_id}']
- # check all recorded designs have matching pieces (at least via alts)
+ # check all recorded designs have matching bricks (at least via alts)
for design_id in self.designs:
- if not [p for p in self.pieces.values()
+ if not [p for p in self.bricks.values()
if p.design_id == design_id]:
- fails += [f'missing Pieces for design of ID: {design_id}']
+ fails += [f'missing bricks for design of ID: {design_id}']
- # check all pieces' colors are recorded
- for color_id in [v.color_id for v in self.pieces.values()
+ # check all bricks' colors are recorded
+ for color_id in [v.color_id for v in self.bricks.values()
if v.color_id not in self.colors]:
- fails += [f'missing Color of ID: {color_id}']
+ fails += [f'missing color of ID: {color_id}']
- # check collections in-out directions even out
+ # check sets' in-out directions even out
counts: dict[str, int] = {}
- for coll in [coll for coll in self.collections.values()
+ for coll in [coll for coll in self.sets.values()
if coll.is_in is not None]:
- for count, piece_id, _ in coll.piece_listings_flat():
- counts[piece_id] = (counts.get(piece_id, 0)
+ for count, brick_id, _ in coll.brick_listings_flat():
+ counts[brick_id] = (counts.get(brick_id, 0)
+ ((1 if coll.is_in else (-1)) * count))
- for piece_id, count in [(k, v) for k, v in counts.items()
+ for brick_id, count in [(k, v) for k, v in counts.items()
if v != 0]:
- fails += [f'invalid count for Piece of ID: {piece_id} ({count})']
+ fails += [f'invalid count for brick of ID: {brick_id} ({count})']
# print, and crash on, collected fails, if any
for fail in fails: