class Textfiled(ABC):
- 'Table to be read from textfile.'
+ 'Table to be read from textfile, and compatible output in .raw().'
@staticmethod
def lines_of(
) -> dict[str, Self]:
'Build from file at path.'
+ @abstractmethod
+ def raw(self) -> str:
+ 'Output in format used for reading in.'
+
+ def __str__(
+ self
+ ) -> str:
+ return self.raw()
-class WithDb(ABC):
+
+class WithDb:
'Add db:Optional[BricksDB] field to __init__, setting ._db.'
def __init__(
super().__init__(**kwargs)
-class Color(Textfiled):
+class Lookupable:
+ 'Provide methods used by BricksDb.lookup.'
+
+ def searchable(
+ self
+ ) -> str:
+ 'String analyzed by "?" full-text searches.'
+ return str(self)
+
+ def show(
+ self
+ ) -> str:
+ 'Default single-item display of self.'
+ return str(self)
+
+
+class Color(Textfiled, Lookupable):
'Color incl. solidness/transparency field.'
def __init__(
collected[id_] = cls(id_, desc[0] == CHAR_COL_SOLID, desc[1:])
return collected
- def __str__(
+ def raw(
self
) -> str:
return (f'{self.id_:>3} '
+ self.wavelength)
-class Design(Textfiled):
+class Design(Textfiled, Lookupable):
'Shape and texture configurations with descriptions and equalities.'
def __init__(
collected[id_].alternates = alternatives
return collected
- def __str__(
+ def raw(
self
) -> str:
return '\n'.join([f'{self.id_:>6} _{self.description}']
return None
-class Piece(Textfiled):
+class Piece(Textfiled, Lookupable):
'Individual configuration of design and color.'
def __init__(
collected[piece_id] = cls(piece_id, design_id, color_id, comment)
return collected
- def __str__(
+ def raw(
self
) -> str:
return (f'{self.id_:>7} {self.design_id:>6} '
f'{self.color_id:>3} {self.comment}').rstrip()
-class Collection(Textfiled, WithDb):
+class Collection(Textfiled, WithDb, Lookupable):
'Named collection of pieces in order of pages of columns of counts.'
def __init__(
db=db)
for k, v in collected.items()}
- def __str__(
+ @property
+ def _is_in_str(
+ self
+ ) -> str:
+ return (CHAR_COLL_INACTIVE if self.is_in is None
+ else (CHAR_COLL_IN if self.is_in else CHAR_COLL_OUT))
+
+ def raw(
self
) -> str:
- is_in_str = (CHAR_COLL_INACTIVE if self.is_in is None
- else (CHAR_COLL_IN if self.is_in else CHAR_COLL_OUT))
- return (f'{self.id_} {is_in_str}{self.description}\n'
+ return (f'{self.id_} {self._is_in_str}{self.description}\n'
+ self._format_paginated(lambda count, p_id, comment:
f' {count:2} {p_id:>7} {comment}'))
+ def show(
+ self
+ ) -> str:
+ return self.raw()
+
+ def __str__(
+ self
+ ) -> str:
+ return f'{self.id_} {self._is_in_str} {self.description}'
+
+ def searchable(
+ self
+ ) -> str:
+ return self.raw()
+
def _format_paginated(
self,
format_line: Callable[[int, str, str], str]
print(line)
-class Box(WithDb):
+class Box(WithDb, Lookupable):
'Order of designs.'
def __init__(
) -> str:
return f'{self.id_:>2} {",".join(self.designs)}'
- def print_pieces(
+ def show(
self,
- ) -> None:
- 'Print explanatory listings of pieces expected by .designs.'
+ ) -> str:
assert self._db
- print(f'box:{self.id_}')
+ lines = []
for design_id in self.designs:
design = self._db.designs[design_id]
- print(f'=== {design_id:>6}: {design.description} ===')
+ lines += [f'=== {design_id:>6}: {design.description} ===']
for piece in [p for p in self._db.pieces.values()
if p.design_id in {design.id_} | design.alternates]:
count_pieces = 0
for c in self._db.collections.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}')
+ lines += [f'{count_pieces:>2}× {piece.id_:>7} / {color}']
+ return '\n'.join(lines)
+
+ def raw(
+ self
+ ) -> str:
+ 'Call .raw() of base Collection.'
+ assert self._db
+ return self._db.collections[f'box:{self.id_}'].raw()
class BricksDb:
def lookup(
self,
table_name: str,
- inquiry: str
+ inquiry: str,
+ show_raw: bool
) -> str:
'Return result of inquiry on table of table_name.'
assert table_name in self.lookupable
maybe_dict = getattr(self, table_name)
table = maybe_dict if isinstance(maybe_dict, dict) else maybe_dict()
- return (CHAR_NEWLINE.join([str(v) for
- v in sorted(table.values(),
- key=lambda v: f'{v.id_:>7}')
- if inquiry[1:].upper() in str(v).upper()])
- if inquiry.startswith(CHAR_INQUIRY)
- else table[inquiry])
+ if inquiry.startswith(CHAR_INQUIRY):
+ return CHAR_NEWLINE.join(
+ [(v.raw() if show_raw else str(v)) for
+ v in sorted(table.values(), key=lambda v: f'{v.id_:>7}')
+ if inquiry[1:].upper() in str(v.searchable()).upper()])
+ item = table[inquiry]
+ return item.raw() if show_raw else item.show()
def piece_to_box_listing(
self,
parser = ArgumentParser()
add_abbrev_choices_arg(parser, 'table', set(db.lookupable))
parser.add_argument('inquiry', nargs='?', default=CHAR_INQUIRY)
+ parser.add_argument('--raw', action='store_true')
args = parser.parse_args()
- print(db.lookup(args.table, args.inquiry))
+ print(db.lookup(args.table, args.inquiry, args.raw))
main()