home · contact · privacy
Differentiate output formats.
authorPlom Heller <plom@plomlompom.com>
Mon, 4 May 2026 04:26:50 +0000 (06:26 +0200)
committerPlom Heller <plom@plomlompom.com>
Mon, 4 May 2026 04:26:50 +0000 (06:26 +0200)
bricksplom.py

index 3532a61d05b79712ccb162477bbab143c7981657..c085448702f939418953656fc0938dc491fc5dfc 100755 (executable)
@@ -34,7 +34,7 @@ Page = tuple[PageColumn, ...]
 
 
 class Textfiled(ABC):
-    'Table to be read from textfile.'
+    'Table to be read from textfile, and compatible output in .raw().'
 
     @staticmethod
     def lines_of(
@@ -73,8 +73,17 @@ class Textfiled(ABC):
             ) -> 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__(
@@ -86,7 +95,23 @@ class WithDb(ABC):
         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__(
@@ -113,7 +138,7 @@ class Color(Textfiled):
             collected[id_] = cls(id_, desc[0] == CHAR_COL_SOLID, desc[1:])
         return collected
 
-    def __str__(
+    def raw(
             self
             ) -> str:
         return (f'{self.id_:>3} '
@@ -121,7 +146,7 @@ class Color(Textfiled):
                 + self.wavelength)
 
 
-class Design(Textfiled):
+class Design(Textfiled, Lookupable):
     'Shape and texture configurations with descriptions and equalities.'
 
     def __init__(
@@ -156,7 +181,7 @@ class Design(Textfiled):
             collected[id_].alternates = alternatives
         return collected
 
-    def __str__(
+    def raw(
             self
             ) -> str:
         return '\n'.join([f'{self.id_:>6} _{self.description}']
@@ -177,7 +202,7 @@ class Design(Textfiled):
         return None
 
 
-class Piece(Textfiled):
+class Piece(Textfiled, Lookupable):
     'Individual configuration of design and color.'
 
     def __init__(
@@ -207,14 +232,14 @@ class Piece(Textfiled):
             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__(
@@ -274,15 +299,35 @@ class Collection(Textfiled, WithDb):
                    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]
@@ -332,7 +377,7 @@ class Collection(Textfiled, WithDb):
             print(line)
 
 
-class Box(WithDb):
+class Box(WithDb, Lookupable):
     'Order of designs.'
 
     def __init__(
@@ -350,15 +395,14 @@ class Box(WithDb):
             ) -> 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
@@ -367,7 +411,15 @@ class Box(WithDb):
                                  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:
@@ -459,18 +511,20 @@ 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,
@@ -522,8 +576,9 @@ def main(
     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()