Skip to content
Snippets Groups Projects
Commit 41cbe8fd authored by Hock, Benedikt's avatar Hock, Benedikt
Browse files

Checked some TODOs

parent ef0fc4d4
No related branches found
No related tags found
No related merge requests found
...@@ -2,97 +2,192 @@ ...@@ -2,97 +2,192 @@
File consists of several classes for the different elements of a device. File consists of several classes for the different elements of a device.
''' '''
from __future__ import annotations from __future__ import annotations
from enum import Enum, auto
import uuid import uuid
from typing import Any, Union, Literal, TypedDict, TypeVar, Type, List, Optional, Dict from typing import Any, Union, Literal, TypedDict, TypeVar, Type, List, Optional, Dict
import json
# TODO # TODO
# - Parents direkt setzen
# - Alle gemeinsamen JSON attribute im LegoItem constructor
# - enforce uuid
# - Docstrings # - Docstrings
# - Beschreibung von Teilen (-> properties) # - Beschreibung von Teilen (-> properties)
# - Gute String Darstellung # - Gute String Darstellung
# - Minimalbeispiel für KPIs # - Minimalbeispiel für KPIs
# - Klassen analog zur deutschen Aufgabenstellung umbenennen (assembly, component) #13 #17
# - Export als JSON
# - Export als GraphViz # - Export als GraphViz
# - AggregationsEbene enum (system, assembly, subassembly, component)
class LegoItem: class ComponentCategory(Enum):
def __init__(self, item_number: int, mass: float, delivery_time: int, **kwargs) -> None: BATTERY = auto()
# , *args, **kwargs not handling additional/optional specs right now MOTOR = auto()
self.id: uuid.UUID = uuid.uuid4() FRAME = auto()
self.properties: dict = kwargs WHEEL = auto()
self.item_number: int = item_number AXLE = auto()
GEAR = auto()
class AggregationLayer(Enum):
SYSTEM = auto()
ASSEMBLY = auto()
SUBASSEMBLY = auto()
COMPONENT = auto()
class LegoComponent:
def __init__(self, name: str, category: ComponentCategory, lego_id: str, cost: float, mass: float,
delivery_time: int, layer: AggregationLayer = AggregationLayer.COMPONENT, **properties) -> None:
self.uuid: uuid.UUID = uuid.uuid4()
self.parent: None | LegoAssembly = None
self.name: str = name
self.category: ComponentCategory = category
self.lego_id: str = lego_id
self.cost: float = cost
self.mass: float = mass self.mass: float = mass
self.delivery_time: int = delivery_time self.delivery_time: int = delivery_time
# TODO: Set parent directly and not via id? This would allow for easier traversal of the tree self.layer: AggregationLayer = layer
# Currently there is no way to search for parts and components in tree by id. self.properties: dict = properties
self.parent_id = None # This will be set when added to a component
def clone(self) -> LegoComponent:
clone = LegoComponent(self.name, self.category, self.lego_id, self.cost, self.mass, self.delivery_time,
self.layer, **self.properties)
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:
ATTRIBUTES = ["uuid", "name", "category", "lego_id", "cost", "mass", "delivery_time", "layer", "properties"]
dict_ = {}
# store attributes
for attr in ATTRIBUTES:
dict_[attr] = getattr(self, attr)
dict_ = {"component": dict_}
return dict_
# TODO good string representation
def __str__(self): def __str__(self):
return self.__repr__()
return ( return (
f"Item(id={self.id}, item_number={self.item_number}, " f"Item(id={self.uuid}, item_number={self.lego_id}, "
f"mass={self.mass}, delivery_time={self.delivery_time}, " f"mass={self.mass}, delivery_time={self.delivery_time}, "
f"parent_id={self.parent_id})" f"parent_id={self.parent})"
) )
# TODO good repr representation
def __repr__(self): def __repr__(self):
return f"Lego Item [{self.id}]" return f"LegoComponent {self.name} [{self.uuid}]"
class LegoComponent: class LegoAssembly:
def __init__(self, **kwargs) -> None: def __init__(self, name: str, layer: AggregationLayer, **properties) -> None:
self.id: uuid.UUID = uuid.uuid4() self.uuid: uuid.UUID = uuid.uuid4()
self.properties: dict = kwargs self.parent: None | LegoAssembly = None
self.items: List[LegoItem] = [] self.name: str = name
self.layer: AggregationLayer = layer
self.properties: dict = properties
self.components: List[LegoComponent] = [] self.components: List[LegoComponent] = []
self.parent_id: None | uuid.UUID = None self.assemblies: List[LegoAssembly] = []
def add_item(self, item: LegoItem) -> None:
if not isinstance(item, LegoItem):
raise TypeError(f"'item' should be of type LegoPart, got {type(item).__name__} instead.")
item.parent_id = self.id
self.items.append(item)
def add_component(self, component: LegoComponent) -> None: def add_component(self, component: LegoComponent) -> None:
if not isinstance(component, LegoComponent): if not isinstance(component, LegoComponent):
raise TypeError(f"'component' should be of type LegoComponent, got {type(component).__name__} instead.") raise TypeError(f"Argument should be of type {LegoComponent.__name__}, "
component.parent_id = self.id 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) self.components.append(component)
def children(self) -> Dict[str, List[LegoItem] | List[LegoComponent]]: def add_assembly(self, assembly: LegoAssembly) -> None:
return {'items': self.items, 'components': self.components} 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_item_list(self) -> List[LegoItem]: def get_root_assembly(self) -> LegoAssembly:
item_list = [] current_assembly = self
item_list.extend(self.items) while current_assembly.parent is not None:
for component in self.components: current_assembly = current_assembly.parent
item_list.extend(component.get_item_list()) return current_assembly
return item_list
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:
ATTRIBUTES = ["uuid", "name", "layer", "properties"]
dict_ = {}
# store attributes
for attr in ATTRIBUTES:
dict_[attr] = getattr(self, attr)
# 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): def __repr__(self):
return f"Lego Component [{self.id}]" return f"LegoAssembly {self.name} [{self.uuid}]"
# TODO: Adjust default output when printing an item or component
def print_component_tree(root, level=0, is_last=False): def print_assembly_tree(root, level=0, is_last=False):
# print component # print component
component_padding = "" assembly_padding = ""
if level > 0: if level > 0:
component_padding += "" * (level - 1) assembly_padding += "" * (level - 1)
if is_last: if is_last:
component_padding += "└── " assembly_padding += "└── "
else: else:
component_padding += "├── " assembly_padding += "├── "
print(f"{component_padding}{root}") print(f"{assembly_padding}{root}")
# recursively print child components # recursively print child components
for i, component in enumerate(root.components): for i, assembly in enumerate(root.assemblies):
is_last_ = i == len(root.components) - 1 and len(root.items) == 0 is_last_ = i == len(root.assemblies) - 1 and len(root.components) == 0
print_component_tree(component, level + 1, is_last_) print_assembly_tree(assembly, level + 1, is_last_)
# print items # print items
for i, item in enumerate(root.items): for i, item in enumerate(root.components):
item_padding = "" * level component_padding = "" * level if not is_last else " "
item_padding += "├── " if i < len(root.items) - 1 else "└── " component_padding += "├── " if i < len(root.components) - 1 else "└── "
print(f"{item_padding}{item}") print(f"{component_padding}{item}")
class KPIEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, uuid.UUID):
return "kpi-" + str(o)
if isinstance(o, (ComponentCategory, AggregationLayer)):
return "kpi-" + o.name
return super().default(o)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment