@abstractmethod
def from_textfile(
cls,
- path: tuple[str, str]
+ path: tuple[str, str],
+ **kwargs
) -> dict[str, Self]:
'Build from file at path.'
+class WithDb(ABC):
+
+ def __init__(
+ self,
+ db: Optional['BricksDb'] = None,
+ **kwargs
+ ) -> None:
+ self._db = db
+ super().__init__(**kwargs)
+
+
class Color(Textfiled):
'Color incl. solidness/transparency field.'
@classmethod
def from_textfile(
cls,
- path: tuple[str, str]
+ path: tuple[str, str],
+ **_
) -> dict[str, Self]:
collected = {}
for id_, desc in [cls.tokify(line, 2) for line in cls.lines_of(path)]:
@classmethod
def from_textfile(
cls,
- path: tuple[str, str]
+ path: tuple[str, str],
+ **_
) -> dict[str, Self]:
collected = {}
alts: dict[str, set[str]] = {}
@classmethod
def from_textfile(
cls,
- path: tuple[str, str]
+ path: tuple[str, str],
+ **_
) -> dict[str, Self]:
collected = {}
for toks in [cls.tokify(line, 3) for line in cls.lines_of(path)]:
f'{self.color_id:>3} {self.comment}').rstrip()
-class Collection(Textfiled):
+class Collection(Textfiled, WithDb):
'Named collection of pieces in order of pages of columns of counts.'
def __init__(
id_: str,
is_in: Optional[bool],
description: str,
- piece_listings: tuple[Page, ...]
+ piece_listings: tuple[Page, ...],
+ **kwargs
) -> None:
self.id_ = id_
self.is_in = is_in
self.description = description
self.piece_listings = piece_listings
+ super().__init__(**kwargs)
@classmethod
def from_textfile(
cls,
- path: tuple[str, str]
+ path: tuple[str, str],
+ db: Optional['BricksDb'] = None,
+ **_
) -> dict[str, Self]:
collected: dict[str, tuple[Optional[bool],
str,
assert len(id_) > 0
i_listings[-1][-1] += [(int(count), id_, comment)]
return {
- k: cls(k, v[0], v[1], tuple(tuple(tuple(column) for column in page)
- for page in v[2]))
+ k: cls(id_=k,
+ is_in=v[0],
+ description=v[1],
+ piece_listings=tuple(tuple(tuple(column) for column in page)
+ for page in v[2]),
+ db=db)
for k, v in collected.items()}
def __str__(
collected += list(column)
return tuple(collected)
- def _piece_to_box_idx_and_description(
- self,
- piece: 'Piece',
- designs: dict[str, 'Design'],
- boxes: dict[str, 'Box']
- ) -> tuple[str, str]:
- design = Design.possibly_by_alt(piece.design_id, designs)
- assert design is not None
- description = design.description
- box = [b for b in boxes.values() if design.id_ in b.designs][0]
- idx_in_box = box.designs.index(design.id_) + 1
- return f'{box.id_:>2}:{idx_in_box:>2}', description
-
def print_packing_instructions(
- self,
- pieces: dict[str, 'Piece'],
- designs: dict[str, 'Design'],
- boxes: dict[str, 'Box'],
+ self
) -> None:
'Print helper for putting into boxes all pieces of self.'
def format_line(_, piece_id, __) -> str:
- box_listing, description = self._piece_to_box_idx_and_description(
- pieces[piece_id], designs, boxes)
+ assert self._db
+ box_listing, description = self._db.piece_to_box_listing(piece_id)
return f'{piece_id:>7} {box_listing} {description}'
print(self._format_paginated(format_line))
def print_unpacking_instructions(
- self,
- pieces: dict[str, 'Piece'],
- designs: dict[str, 'Design'],
- boxes: dict[str, 'Box'],
- colors: dict[str, 'Color']
+ self
) -> None:
'Print helper for collecting from boxes all pieces of self.'
+ assert self._db
lines = []
for count, piece_id, _ in self.piece_listings_flat():
- piece = pieces[piece_id]
- box_listing, description = self._piece_to_box_idx_and_description(
- piece, designs, boxes)
- color = str(colors[piece.color_id]).lstrip().split(CHAR_SPACE,
- maxsplit=1)[1]
+ box_listing, description = self._db.piece_to_box_listing(piece_id)
+ color = str(self._db.colors[self._db.pieces[piece_id].color_id]
+ ).lstrip().split(CHAR_SPACE, maxsplit=1)[1]
lines += [f'{box_listing} {count}× {color} {description}']
for line in sorted(lines):
print(line)
-class Box:
+class Box(WithDb):
'Order of designs.'
def __init__(
self,
id_: str,
- designs: tuple[str, ...]
+ designs: tuple[str, ...],
+ **kwargs
) -> None:
self.id_ = id_
self.designs = designs
-
- @classmethod
- def from_collections(
- cls,
- collections: dict[str, 'Collection'],
- pieces: dict[str, 'Piece'],
- designs: dict[str, 'Design']
- ) -> dict[str, Self]:
- 'Parse from "box:"-prefixed entries in collections.'
- collected = {}
- for coll in [c for c in collections.values()
- if c.id_.startswith(BOX_PREFIX)]:
- box_id = coll.id_[len(BOX_PREFIX):]
- assert box_id
- boxed_designs: list[str] = []
- for piece_id in [t[1] for t in coll.piece_listings_flat()]:
- design = Design.possibly_by_alt(pieces[piece_id].design_id,
- designs)
- assert design
- if [design.id_] != boxed_designs[-1:]:
- boxed_designs += [design.id_]
- collected[box_id] = cls(box_id, tuple(boxed_designs))
- return collected
+ super().__init__(**kwargs)
def __str__(
self
def print_pieces(
self,
- pieces: dict[str, 'Piece'],
- designs: dict[str, 'Design'],
- colors: dict[str, 'Color'],
- collections: dict[str, 'Collection']
) -> None:
'Print explanatory listings of pieces expected by .designs.'
- print(f'box:{self.id_} - storage box')
+ assert self._db
+ print(f'box:{self.id_}')
for design_id in self.designs:
- design = designs[design_id]
+ design = self._db.designs[design_id]
print(f'=== {design_id:>6}: {design.description} ===')
- for piece in [p for p in pieces.values()
+ for piece in [p for p in self._db.pieces.values()
if p.design_id in {design.id_} | design.alternates]:
count_pieces = 0
- color = colors[piece.color_id]
+ color = self._db.colors[piece.color_id]
for listings in [c.piece_listings_flat()
- for c in collections.values()]:
+ for c in self._db.colls.values()]:
for count in [t[0] for t in listings if t[1] == piece.id_]:
count_pieces += count
print(f'{count_pieces:>2}× {piece.id_:>7} / {color}')
-def check_consistencies_between_tables(
- colors: dict[str, Color],
- pieces: dict[str, Piece],
- collections: dict[str, Collection],
- designs: dict[str, Design],
- boxes: dict[str, Box]
- ) -> None:
- 'Ensure intra-table consistencies between inputs.'
-
- # check all items listed in collections recorded in pieces
- for coll in collections.values():
- for _, piece_id, _ in coll.piece_listings_flat():
- assert piece_id in pieces, piece_id
-
- # check all designs listed in boxes recorded in designs
- for design_ids in [b.designs for b in boxes.values()]:
- for design_id in design_ids:
- assert design_id in designs, design_id
-
- # check all pieces' designs recorded in designs
- for design_id in [piece.design_id for piece in pieces.values()]:
- assert Design.possibly_by_alt(design_id, designs), design_id
-
- # check all recorded designs have matching pieces (at least via alts)
- for design_id, alts in [(k, v.alternates) for k, v in designs.items()]:
- pieces_found = False
- for id_ in [id_ for id_ in {design_id} | alts
- if id_ in [piece.design_id for piece in pieces.values()]]:
- pieces_found = True
- break
- assert pieces_found, design_id
-
- # check all pieces' colors are recorded
- for color_id in [v.color_id for v in pieces.values()]:
- assert color_id in colors, color_id
-
- # check collections in-out directions even out
- counts: dict[str, int] = {}
- for coll in collections.values():
- if coll.is_in is None:
- continue
- for count, piece_id, _ in coll.piece_listings_flat():
- counts[piece_id] = (counts.get(piece_id, 0)
- + ((1 if coll.is_in else (-1)) * count))
- for piece_id, count in counts.items():
- assert count == 0, (piece_id, count)
+class BricksDb:
+ 'Collection of all the tables enabling their combined processing.'
+
+ 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))
+ self.colls = Collection.from_textfile((path_tables, PATH_COLLECTIONS),
+ db=self)
+ self.designs = Design.from_textfile((path_tables, PATH_DESIGNS))
+ self._check_consistencies_between_tables()
+
+ def boxes(
+ self
+ ) -> dict[str, Box]:
+ 'Parsed from "box:"-prefixed entries in .colls.'
+ collected = {}
+ for coll in [c for c in self.colls.values()
+ if c.id_.startswith(BOX_PREFIX)]:
+ box_id = coll.id_[len(BOX_PREFIX):]
+ assert box_id
+ boxed_designs: list[str] = []
+ for piece_id in [t[1] for t in coll.piece_listings_flat()]:
+ design = Design.possibly_by_alt(
+ self.pieces[piece_id].design_id,
+ self.designs)
+ assert design
+ if [design.id_] != boxed_designs[-1:]:
+ boxed_designs += [design.id_]
+ collected[box_id] = Box(box_id, tuple(boxed_designs), db=self)
+ return collected
+
+ def _check_consistencies_between_tables(
+ self
+ ) -> None:
+
+ # check all items listed in collections recorded in pieces
+ for coll in self.colls.values():
+ for _, piece_id, _ in coll.piece_listings_flat():
+ assert piece_id in self.pieces, piece_id
+
+ # check all designs listed in boxes recorded in designs
+ for design_ids in [b.designs for b in self.boxes().values()]:
+ for design_id in design_ids:
+ assert design_id in self.designs, design_id
+
+ # check all pieces' designs recorded in designs
+ for design_id in [piece.design_id for piece in self.pieces.values()]:
+ assert Design.possibly_by_alt(design_id, self.designs), design_id
+
+ # check all recorded designs have matching pieces (at least via alts)
+ for design_id, alts in [(k, v.alternates)
+ for k, v in self.designs.items()]:
+ pieces_found = False
+ for id_ in [id_ for id_ in {design_id} | alts
+ if id_ in [piece.design_id
+ for piece in self.pieces.values()]]:
+ pieces_found = True
+ break
+ assert pieces_found, design_id
+
+ # check all pieces' colors are recorded
+ for color_id in [v.color_id for v in self.pieces.values()]:
+ assert color_id in self.colors, color_id
+
+ # check collections in-out directions even out
+ counts: dict[str, int] = {}
+ for coll in self.colls.values():
+ if coll.is_in is None:
+ continue
+ for count, piece_id, _ in coll.piece_listings_flat():
+ counts[piece_id] = (counts.get(piece_id, 0)
+ + ((1 if coll.is_in else (-1)) * count))
+ for piece_id, count in counts.items():
+ assert count == 0, (piece_id, count)
+
+ def piece_to_box_listing(
+ self,
+ piece_id: str,
+ ) -> tuple[str, str]:
+ 'For Piece of piece_id, print bosition in .boxes() and description.'
+ design = Design.possibly_by_alt(self.pieces[piece_id].design_id,
+ self.designs)
+ assert design is not None
+ description = design.description
+ box = [b for b in self.boxes().values() if design.id_ in b.designs][0]
+ idx_in_box = box.designs.index(design.id_) + 1
+ return f'{box.id_:>2}:{idx_in_box:>2}', description
+
+ def print(
+ self
+ ) -> None:
+ 'Show all we know.'
+ for title, items in (('COLORS', self.colors),
+ ('PIECES', self.pieces),
+ ('DESIGNS', self.designs),
+ ('BOXES', self.boxes()),
+ ('COLLECTIONS', self.colls)):
+ print(title)
+ for item in items.values():
+ print(item)
def main(
) -> None:
'Load tables from BRICKSPLOM_DIR and put out in reproducible listings.'
- dir_tables = environ.get(NAME_ENV_DIRNAME, '.')
- colors = Color.from_textfile((dir_tables, PATH_COLORS))
- pieces = Piece.from_textfile((dir_tables, PATH_PIECES))
- collections = Collection.from_textfile((dir_tables, PATH_COLLECTIONS))
- designs = Design.from_textfile((dir_tables, PATH_DESIGNS))
- boxes = Box.from_collections(collections, pieces, designs)
- check_consistencies_between_tables(colors, pieces, collections, designs,
- boxes)
- for title, items in (('COLORS', colors),
- ('PIECES', pieces),
- ('DESIGNS', designs),
- ('BOXES', boxes),
- ('COLLECTIONS', collections)):
- print(title)
- for item in items.values():
- print(item)
+ db = BricksDb(environ.get(NAME_ENV_DIRNAME, '.'))
+ db.print()
main()