'Data structures for managing/sorting bricks of a certain kind.'
from abc import ABC, abstractmethod
from argparse import ArgumentError, ArgumentParser
+from dataclasses import dataclass
from os import environ
from pathlib import Path
from typing import Callable, Optional, Self
+ self.wavelength)
+@dataclass
class BrickDesignData:
- 'Whatever be available in BrickDesign.attrs.'
-
- def __init__(
- self,
- n_studs=-1,
- description=''
- ) -> None:
- self.description = description
- self.n_studs = n_studs
+ 'Lookupables for BrickDesign besides .id.'
+ n_studs: int = -1
+ description: str = ''
class BrickDesign(Textfiled, Lookupable):
attrs: Optional[BrickDesignData] = None
) -> None:
self.id_ = id_
- self._attrs = attrs
+ self.direct_attrs = attrs
self.alternate_ids: set[str] = set()
- @property
- def attrs(
- self
- ) -> BrickDesignData:
- 'Collect from either .alternate_to or ._attrs.'
- if self.alternate_to:
- return self.alternate_to.attrs
- assert self._attrs
- return self._attrs
+ def __getattribute__(self, key: str):
+ if key in BrickDesignData.__annotations__:
+ if self.direct_attrs:
+ attrs = self.direct_attrs
+ else:
+ assert self.alternate_to is not None
+ assert self.alternate_to.direct_attrs is not None
+ attrs = self.alternate_to.direct_attrs
+ return getattr(attrs, key)
+ return super().__getattribute__(key)
@property
def all_ids(
q_body
) -> bool:
assert q_body.isdigit()
- return self.attrs.n_studs == int(q_body)
+ return self.n_studs == int(q_body)
return super().matchers | {PARAM_Q_STUDS: q_studs}
@property
return super().sorters | {
TOK_SORT_BOX: self._by_box_sorter('design'),
TOK_SORT_STUDS: lambda _, pre_sorted: tuple(
- sorted(pre_sorted, key=lambda item: item.attrs.n_studs))
+ sorted(pre_sorted, key=lambda item: item.n_studs))
}
@classmethod
else:
assert SEP_DESIGN_DESC in body
metadata, desc = body.split(SEP_DESIGN_DESC, maxsplit=1)
- kwargs: dict[str, str | int] = {'description': desc}
+ attrs = BrickDesignData(description=desc)
+ annos = BrickDesignData.__annotations__
for attr in metadata.split(SEP_DESIGN_ATTR):
assert CHAR_ATTR_EQ in attr
a_key, a_val_str = attr.split(CHAR_ATTR_EQ, maxsplit=1)
+ assert a_key in annos and annos[a_key] is int
assert a_val_str.isdigit()
- kwargs[a_key] = int(a_val_str)
- collected[design_id] = cls(design_id,
- BrickDesignData(**kwargs))
+ setattr(attrs, a_key, int(a_val_str))
+ collected[design_id] = cls(design_id, attrs)
for id_, alternate_ids in alts.items():
collected[id_].alternate_ids = alternate_ids
for alt_id in alternate_ids:
return (
f'{self.id_indented()} '
+ (f'{CHAR_DESIGN_ALT}{self.alternate_to.id_}' if self.alternate_to
- else (('' if self.attrs.n_studs < 0
- else f'n_studs{CHAR_ATTR_EQ}{self.attrs.n_studs}')
- + f'{SEP_DESIGN_DESC}{self.attrs.description}')))
+ else (('' if self.n_studs < 0
+ else f'n_studs{CHAR_ATTR_EQ}{self.n_studs}')
+ + f'{SEP_DESIGN_DESC}{self.description}')))
class Brick(Textfiled, WithDb, Lookupable):
comment = f' # {self.comment}' if self.comment else ''
return (f'{self.id_indented()} '
f'{BrickDesign.indent_id(self.design_id)} '
- f'{design.attrs.description} ({color}){comment}')
+ f'{design.description} ({color}){comment}')
class BrickSet(Textfiled, WithDb, Lookupable):
return (
f'{count:>2}× {Brick.indent_id(brick_id)}:'
f'{BrickDesign.indent_id(design_id)} {box_listing} {color} '
- + self._db.designs[design_id].attrs.description + tail_comment)
+ + self._db.designs[design_id].description + tail_comment)
return f'{self}{CHAR_NEWLINE}{self._format_paginated(format_line)}'
lines = []
for design, listings in self.designs_to_listings:
lines += [
- f'=== {" / ".join(design.all_ids)}: '
- f'{design.attrs.description} ===']
+ f'=== {" / ".join(design.all_ids)}: {design.description} ===']
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} '