""" File consists of several classes for the different elements of a device. """ from __future__ import annotations from enum import Enum, auto import uuid from typing import List, Dict import json import copy # TODO # - Docstrings # - Minimalbeispiel für KPIs -> halb umgesetzt -> mit get_components veranschaulichen # - Erlaube Clone bei Assembly (jedes child muss durch durch Klon ersetzt werden) # - Änderungen an Beispiel umsetzen # - Gute String Darstellung -> Ist so schon ok bisher? -> Nä Semester # - Export als GraphViz -> Nä Semeseter class AggregationLayer(Enum): SYSTEM = auto() ASSEMBLY = auto() SUBASSEMBLY = auto() COMPONENT = auto() class LegoComponent: def __init__( self, label: str, properties: dict, layer: AggregationLayer = AggregationLayer.COMPONENT, ) -> None: self.uuid: uuid.UUID = uuid.uuid4() self.parent: None | LegoAssembly = None self.label: str = label self.properties: dict = properties self.layer: AggregationLayer = layer def clone(self, new_description: str = None) -> LegoComponent: if new_description is None: new_description = self.label clone = LegoComponent(new_description, copy.deepcopy(self.properties), self.layer) return clone def get_root_assembly(self): if self.parent is None: return None current_assembly = self.parent while current_assembly.parent is not None: current_assembly = current_assembly.parent return current_assembly def to_dict(self) -> Dict: dict_ = { "uuid": self.uuid, "label": self.label, "properties": self.properties, "layer": self.layer, } return {"component": dict_} # TODO good string representation def __str__(self): return self.__repr__() return ( f"Item(id={self.uuid}, item_number={self.lego_id}, " f"mass={self.mass}, delivery_time={self.delivery_time}, " f"parent_id={self.parent})" ) # TODO good repr representation def __repr__(self): return f"LegoComponent {self.label} [{self.uuid}]" class LegoAssembly: def __init__(self, label: str, properties: dict, layer: AggregationLayer) -> None: self.uuid: uuid.UUID = uuid.uuid4() self.parent: None | LegoAssembly = None self.label: str = label self.properties: dict = properties self.layer: AggregationLayer = layer self.components: List[LegoComponent] = [] self.assemblies: List[LegoAssembly] = [] def add_component(self, component: LegoComponent | List[LegoComponent]) -> None: if isinstance(component, list): for c in component: self.add_component(c) return if not isinstance(component, LegoComponent): raise TypeError( f"Argument should be of type {LegoComponent.__name__}, " f"got {type(component).__name__} instead." ) if self.get_root_assembly().contains_uuid(component.uuid): raise AssertionError( f"This assembly or a subassembly already contains the component with ID " f"{component.uuid}." ) component.parent = self self.components.append(component) def add_assembly(self, assembly: LegoAssembly | List[LegoAssembly]) -> None: if isinstance(assembly, list): for a in assembly: self.add_assembly(a) return if not isinstance(assembly, LegoAssembly): raise TypeError( f"Argument should be of type {LegoAssembly.__name__}, " f"got {type(assembly).__name__} instead." ) if self.get_root_assembly().contains_uuid(assembly.uuid): raise AssertionError( f"This assembly or a subassembly already contains the assembly with ID " f"{assembly.uuid}." ) assembly.parent = self self.assemblies.append(assembly) def children(self) -> Dict[str, List[LegoComponent] | List[LegoAssembly]]: return {"components": self.components, "assemblies": self.assemblies} def get_component_list(self, max_depth: int = -1) -> List[LegoComponent]: component_list = [] component_list.extend(self.components) if max_depth > 0: for assembly in self.assemblies: component_list.extend(assembly.get_component_list(max_depth - 1)) return component_list def get_root_assembly(self) -> LegoAssembly: current_assembly = self while current_assembly.parent is not None: current_assembly = current_assembly.parent return current_assembly def contains_uuid(self, uuid_: uuid.UUID): # check component ids component_ids = list(map(lambda c: c.uuid, self.components)) if uuid_ in component_ids: return True # check assembly ids assembly_ids = list(map(lambda a: a.uuid, self.assemblies)) if uuid_ in assembly_ids: return True # recursively check assemblies for assembly in self.assemblies: if assembly.contains_uuid(uuid_): return True return False def to_dict(self) -> Dict: dict_ = { "uuid": self.uuid, "label": self.label, "properties": self.properties, "layer": self.layer, } # store components dict_["components"] = [component.to_dict() for component in self.components] dict_["assemblies"] = [assembly.to_dict() for assembly in self.assemblies] return {"assembly": dict_} # TODO find good string representation def __repr__(self): return f"LegoAssembly {self.label} [{self.uuid}]" def print_assembly_tree(root, level=0, is_last=False): # print component assembly_padding = "" if level > 0: assembly_padding += "│ " * (level - 1) if is_last: assembly_padding += "└── " else: assembly_padding += "├── " print(f"{assembly_padding}{root}") # recursively print child components for i, assembly in enumerate(root.assemblies): is_last_ = i == len(root.assemblies) - 1 and len(root.components) == 0 print_assembly_tree(assembly, level + 1, is_last_) # print items for i, item in enumerate(root.components): component_padding = "│ " * level if not is_last else " " component_padding += "├── " if i < len(root.components) - 1 else "└── " print(f"{component_padding}{item}") def check_aggregation_hierarchy(root): pass class KPIEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, uuid.UUID): return "kpi-" + str(o) if isinstance(o, AggregationLayer): return "kpi-" + o.name return super().default(o) pass