class PrintingNode(BaseNode['PrintingNode', Printing]): _children: FrozenMultiset[PrintingNodeChild] _CARDBOARD_EQUIVALENT = CardboardNode flattened: t.Iterator[t.Union[Printing, AnyNode]] flattened_options: t.Iterator[FrozenMultiset[Printing]] def __init__( self, children: t.Union[ t.Iterable[PrintingNodeChild], t.Mapping[PrintingNodeChild, int] ], ): self._children = FrozenMultiset(children) @property def children(self) -> FrozenMultiset[PrintingNodeChild]: return self._children def _calc_persistent_hash(self) -> t.Iterable[t.ByteString]: yield self.__class__.__name__.encode('UTF-8') for s in sorted( child.persistent_hash() if isinstance(child, BaseNode) else str(child.id) for child in self._children ): yield s.encode('ASCII') @property def as_cardboards(self) -> CardboardNode: return self._CARDBOARD_EQUIVALENT( child.cardboard if isinstance(child, Printing) else child.as_cardboards for child in self._children ) def get_minimal_string(self, identified_by_id: bool = True) -> str: return self._MINIMAL_STRING_CONNECTOR.join( ( '({})'.format( (f'{multiplicity}# ' if multiplicity > 1 else '') + f'{child.cardboard.name}|{child.id if identified_by_id else child.expansion.code}' if isinstance(child, Printing) else f'{child.get_minimal_string(identified_by_id)}' ) for child, multiplicity in self.sorted_items ) ) @LazyProperty def name(self): return ''.join( ( str(multiplicity) + 'x' if multiplicity > 1 else '' ) + ( option.cardboard.name if isinstance(option, Printing) else '{} {}'.format( option.name, option.__class__.__name__, ) ) for option, multiplicity in self.sorted_items ) @LazyProperty def sorted_items(self) -> t.List[t.Tuple[PrintingNodeChild, int]]: return sorted( self._children.items(), key = lambda p: p[0].cardboard.name if isinstance(p[0], Printing) else p[0].name ) @property def imageds(self) -> t.Iterator[t.Union[Printing, PrintingNode]]: return itertools.chain( *( itertools.repeat(item, 1 if isinstance(item, Printing) else multiplicity) for item, multiplicity in self._children.items() ) ) @LazyProperty def sorted_imageds(self) -> t.List[PrintingNodeChild]: return sorted( list(self.imageds), key = lambda p: p.cardboard.name if isinstance(p, Printing) else p.name ) @LazyProperty def sorted_uniques(self) -> t.List[PrintingNodeChild]: return sorted( self._children.distinct_elements(), key = lambda p: p.cardboard.name if isinstance(p, Printing) else p.name ) @abstractmethod def get_image(self, loader: ImageLoader, width: int, height: int, **kwargs) -> Image.Image: pass @classmethod def deserialize(cls, value: serialization_model, inflator: Inflator) -> PrintingNode: return ( AnyNode if value['type'] == AnyNode.__name__ else AllNode )( { ( inflator.inflate(Printing, child) if isinstance(child, int) else cls.deserialize(child, inflator) ): multiplicity for child, multiplicity in value['options'] } ) @property def image_amount(self) -> int: return sum( 1 if isinstance(child, Printing) else multiplicity * child.image_amount for child, multiplicity in self._children.items() )
class CardboardNode(BaseNode['CardboardNode', Cardboard]): flattened: t.Iterator[t.Union[Cardboard, CardboardAnyNode]] flattened_options: t.Iterator[FrozenMultiset[Cardboard]] def __init__( self, children: t.Union[ t.Iterable[CardboardNodeChild], t.Mapping[CardboardNodeChild, int] ], ): self._children = FrozenMultiset(children) @property def sorted_items(self) -> t.List[t.Tuple[CardboardNodeChild, int]]: return sorted( self._children.items(), key = lambda p: p[0].name, ) def get_minimal_string(self, **kwargs) -> str: return self._MINIMAL_STRING_CONNECTOR.join( ( '({})'.format( (f'{multiplicity}# ' if multiplicity > 1 else '') + child.name if isinstance(child, Cardboard) else f'{child.get_minimal_string()}' ) for child, multiplicity in self.sorted_items ) ) @property def children(self) -> FrozenMultiset[CardboardNodeChild]: return self._children @property def name(self) -> str: return ''.join( ( str(multiplicity) + 'x' if multiplicity > 1 else '' ) + ( option.name if isinstance(option, Cardboard) else '{} {}'.format( option.name, option.__class__.__name__, ) ) for option, multiplicity in self.sorted_items ) def _calc_persistent_hash(self) -> t.Iterable[t.ByteString]: yield self.__class__.__name__.encode('UTF-8') for s in sorted( child.persistent_hash() if isinstance(child, BaseNode) else str(child.id) for child in self._children ): yield s.encode('UTF-8') @classmethod def deserialize(cls, value: serialization_model, inflator: Inflator) -> CardboardNode: return ( CardboardAnyNode if value['type'] == CardboardAnyNode.__name__ else CardboardAllNode )( { ( inflator.inflate(Cardboard, child) if isinstance(child, str) else cls.deserialize(child, inflator) ): multiplicity for child, multiplicity in value['options'] } )