diff --git a/ausarbeitung.ipynb b/ausarbeitung.ipynb index bb28dfcfd5257098dacbf9699c7bac2b6d67f545..854341402f8d55bde092a8f8a27f999393f8272c 100644 --- a/ausarbeitung.ipynb +++ b/ausarbeitung.ipynb @@ -2,50 +2,351 @@ "cells": [ { "cell_type": "markdown", - "id": "9d0d63c1", + "id": "c9328cd1", "metadata": {}, "source": [ - "# FAIRe Qualitäts-KPIs" + "# FAIRe Qualitäts-KPIs\n", + "Name:\n", + "Datum:" + ] + }, + { + "cell_type": "markdown", + "id": "2ee8746d", + "metadata": {}, + "source": [ + "## Einführung\n", + "FAIRe Qualitäts-KPIs schaffen eine transparente und nachvollziehbare Entscheidungsgrundlage. In dieser Lerneinheit sollen sie die Methodik erlernen, indem sie LEGO-Autos zusammenbauen, deren Qualitäts-KPIs bestimmen und miteinander vergleichen.\n", + "\n", + "### Werkzeug für den Zusammenbau\n", + "Der Zusammenbau der Autos erfolgt virtuell. Als Werkzeug steht Ihnen das Modul `classes` zur Verfügung. In diesem sind unterschiedliche Klassen und Methoden implementiert (Objektorientierung). Sie erzeugen jeweils Objekte der Klassen. Ein Objekt ist dabei die virtuelle Abbildung eines realen Bauteiles. Die Eigenschaften des Bauteiles werden über die Eigenschaften des Objektes abgebildet.\n", + "\n", + "### Berechnung der Qualitäts-KPIs\n", + "KPIs (Key Performance Indikatoren) sind Kenngrößen des Systems. Sie werden über Berechnungsvorschriften (`calculation_rules`) bestimmt. Diese Berechnungsvorschriften sind von Ihnen als python-Funktionen im Modul `calculation_results` zu implementieren.\n", + "\n", + "### Datenblätter\n", + "Für den Zusammenbau stehen Ihnen verschiedene Bauteile in unterschiedlichen Ausführungen zur Verfügung. Sie nutzen die Datenblätter, die als `json-Dateien` zur Verfügung gestellt werden, um Ihre Autos zusammenzubauen." + ] + }, + { + "cell_type": "markdown", + "id": "1c702114", + "metadata": {}, + "source": [ + "## Eigene Module und Minimalbeispiel\n", + "Für die Ausarbeitung nutzen wir zwei eigene Module (Python-Skripte), die im Ordner `functions` gespeichert sind.\n", + "Das Modul `calculation_rules` erweitern Sie während der Ausarbeitung. Um die Änderungen zu nutzen, müssen Sie das Notebook neu starten.\n", + "\n", + "Mit einem Minimalbeispiel wird Ihnen gezeigt, wie sie die Module nutzen." ] }, { "cell_type": "code", - "execution_count": 1, - "id": "c61985be", + "execution_count": null, + "id": "b2778dee", "metadata": {}, "outputs": [], "source": [ "# import modules\n", "from functions import calculation_rules\n", - "from functions import classes" + "from functions import classes\n", + "\n", + "# TO DO import further modules if necessary\n" + ] + }, + { + "cell_type": "markdown", + "id": "3b69752c", + "metadata": {}, + "source": [ + "### Modul calculation_results\n", + "Sie können die unterschiedlichen Funktionen (Berechnungsvorschriften) aufrufen. Beachten Sie dabei die Übergabe- und Rückgabewerte." ] }, { "cell_type": "code", - "execution_count": 2, - "id": "8d0d8deb", + "execution_count": null, + "id": "294c680b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You called the test function.\n" - ] - } - ], + "outputs": [], "source": [ "# test the import\n", "calculation_rules.test_function()" ] }, + { + "cell_type": "markdown", + "id": "a2c87a3c", + "metadata": {}, + "source": [ + "### Modul classes\n", + "Wir bauen ein Auto zusammen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8db386db", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, - "id": "d211b05d", + "id": "8f2ae9f4", "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "markdown", + "id": "de070039", + "metadata": {}, + "source": [ + "## Aufgabe: Aufbau eines ersten Fahrzeugs\n", + "Bauen Sie ein erstes Fahrzeug aus den gegebenen LEGO-Teilen auf.\n", + "Das Fahrzeug muss aus Baugruppen, Bauteilen und Komponenten bestehen. Es muss mindestens vier Räder haben und sich durch den elektrischen Antrieb fortbewegen können." + ] + }, + { + "cell_type": "markdown", + "id": "05a8eb21", + "metadata": {}, + "source": [ + "### Beschreibung des Fahrzeuges\n", + "Beschreiben Sie kurz und präzise Ihr Fahrzeug. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ccaf3043", + "metadata": {}, + "outputs": [], + "source": [ + "# initialize componentens" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ea61422", + "metadata": {}, + "outputs": [], + "source": [ + "# read out properties from datasheets" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36f981df", + "metadata": {}, + "outputs": [], + "source": [ + "# set properties" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24400d94", + "metadata": {}, + "outputs": [], + "source": [ + "# export car and its properties" + ] + }, + { + "cell_type": "markdown", + "id": "c1fef7f0", + "metadata": {}, + "source": [ + "## Aufgabe: Bestimmung der Qualität mittels KPIs\n", + "Bestimmen Sie die Qualität Ihres Fahrzeugs mittels KPIs. \n", + "Die Qualität des Fahrzeugs ist mit mindestens einem KPI je Qualitätsdimension (Aufwand, Verfügbarkeit, Akzeptanz) zu bestimmen. Enwickeln Sie zunächst sinnvolle KPIs, welche mit den gegebenen Daten umsetzbar sind. Halten Sie die Berechnungsvorschriften im Jupyter Notebook fest. Implementieren Sie deren Berechnung für das Gesamtsystem \"Fahrzeug\" mittels einzelner Funktionen im Skript `calculation_rules`. Sie können zusätzlich Ihre Methoden auch auf die niedrigeren Aggregationsebenen anwenden." + ] + }, + { + "cell_type": "markdown", + "id": "d5f02096", + "metadata": {}, + "source": [ + "### Beschreibung KPI\n", + "Beschreiben Sie den jeweiligen KPI und geben Sie seine Berechnungsvorschrift an.\n", + "$$\n", + "a = \\frac{b}{c} + d\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59eabafc", + "metadata": {}, + "outputs": [], + "source": [ + "# calculate the KPIs for your car" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c774b381", + "metadata": {}, + "outputs": [], + "source": [ + "# print your KPIs" + ] + }, + { + "cell_type": "markdown", + "id": "89c75440", + "metadata": {}, + "source": [ + "## Aufgabe: Aufbau eines zweiten Fahrzeugs\n", + "Setzen Sie sich ein Ziel, welche Qualitätsdimensionen in einem zweiten Fahrzeug verbessert werden sollen und bauen Sie dementsprechend ein zweites Fahrzeug aus den gegebenen LEGO-Teilen auf.\n", + "\n", + "Das Fahrzeug muss aus Baugruppen, Bauteilen und Komponenten bestehen. Es muss mindestens vier Räder haben und sich durch den elektrischen Antrieb fortbewegen können. Die Verwendung eines Getriebes zwischen Motor und Antriebsachse(n) ist verpflichtend. Es sind nur die gegebenen LEGO-Teile zu verwenden. " + ] + }, + { + "cell_type": "markdown", + "id": "3cef0828", + "metadata": {}, + "source": [ + "### Beschreibung\n", + "Beschreiben Sie, welche Verbesserung Sie vornehmen.\n", + "..." + ] + }, + { + "cell_type": "markdown", + "id": "73c454f2", + "metadata": {}, + "source": [ + "### Aufbau des zweiten Fahrzeuges\n", + "Beschreiben Sie kurz und präzise den Aufbau des zweiten Fahrzeugs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea18e735", + "metadata": {}, + "outputs": [], + "source": [ + "# build a second car" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3438fc4", + "metadata": {}, + "outputs": [], + "source": [ + "# read out properties from datasheets" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b7336fb", + "metadata": {}, + "outputs": [], + "source": [ + "# set properties" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "446abbca", + "metadata": {}, + "outputs": [], + "source": [ + "# export car and its properties" + ] + }, + { + "cell_type": "markdown", + "id": "89e54480", + "metadata": {}, + "source": [ + "### KPIs des zweiten Fahrzeuges\n", + "Bestimmen Sie die KPIs des zweiten Fahrzeuges" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "762a1e93", + "metadata": {}, + "outputs": [], + "source": [ + "# calculate the KPIs for your car" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ed67328", + "metadata": {}, + "outputs": [], + "source": [ + "# print your KPIs" + ] + }, + { + "cell_type": "markdown", + "id": "e413cd84", + "metadata": {}, + "source": [ + "## Aufgabe: Darstellung und Vergleich der Ergebnisse\n", + "Stellen Sie die Ergebnisse für beide Fahrzeuge übersichtlich dar. \n", + "Die entwickelten KPIs müssen dargestellt und die Datengrundlage ersichtlich sein.\n", + "Eine geeignete grafische Darstellung für die Gegenüberstellung der Ergebnisse für beide Fahrzeuge ist zu wählen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0f93e22", + "metadata": {}, + "outputs": [], + "source": [ + "# plot the data, save diagramm as svg-file" + ] + }, + { + "cell_type": "markdown", + "id": "62ff04b2", + "metadata": {}, + "source": [ + "### Interpretation der Ergebnisse\n", + "Interpretieren Sie ihre Ergebnisse. Vergleichen Sie die KPIs ihrer Autos. Konnten Sie ihre gewünschte Verbesserung erzielen?" + ] + }, + { + "cell_type": "markdown", + "id": "a0840fbf", + "metadata": {}, + "source": [ + "## Aufgabe: Exportieren Sie ihre Daten\n", + "Exportieren/ Speichern Sie alle relevaten Daten ab." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4bdfc7a", + "metadata": {}, + "outputs": [], + "source": [ + "# export all relevant data" + ] } ], "metadata": { diff --git a/datasheets/create_json_from_excel.py b/datasheets/create_json_from_excel.py index af973f419e023e21b47bded8c8aec71858eb0383..0d74bde458406289de3710093fb6b24559607be2 100644 --- a/datasheets/create_json_from_excel.py +++ b/datasheets/create_json_from_excel.py @@ -5,6 +5,8 @@ from collections import namedtuple from typing import Dict, List, NamedTuple import pandas as pd +# pandas is using another dependency called openpyxl +# both need to be installed COLUMN_FOR_INDEX_IN_EXCEL = 0 @@ -14,8 +16,8 @@ HELP_TXT = """ 1. The source excel file. (e.g. test.xlsx) 2. The destination folder for the json file. (e.g. test_folder) 3. Override the folder if existing. (OPTIONAL, False) - - Providing only '-- help' will show this text and exit. + + Providing only '-- help' will show this text and exit. """ diff --git a/datasheets/datasheet_testelement.json b/datasheets/datasheet_testelement.json deleted file mode 100644 index f40fb8479071904686d076bc57d33ed4b84589f0..0000000000000000000000000000000000000000 --- a/datasheets/datasheet_testelement.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "Pumps":[ - {"Name": "Pump_1", - "Manufacturer": "Company A", - "Unit": "Percentage", - "Efficiency": 43}, - {"Name": "Pump_2", - "Manufacturer": "Company B", - "Unit": "Percentage", - "Efficiency": 56 - } - ] -} diff --git a/functions/calculation_rules.py b/functions/calculation_rules.py index e9d20f109763fe70b9c31672035a9869081e63e1..7fa73968992ed1e8fd4d11f16b1479b27f7ae074 100644 --- a/functions/calculation_rules.py +++ b/functions/calculation_rules.py @@ -1,10 +1,19 @@ -''' +""" File consists of several functions for the calculation rules of FAIR Quality KPIs -''' +""" + + def test_function(): print("You called the test function.") +def kpi_sum(*args): + return sum(args[0]) + # if arguments are handed over not as a list: sum(list(args)) + + if __name__ == "__main__": - print("This script contains functions for calculating the FAIR Quality KPIs. It is not to be executed independently.") - pass \ No newline at end of file + print( + "This script contains functions for calculating the FAIR Quality KPIs. It is not to be executed independently." + ) + pass diff --git a/functions/lego_classes.py b/functions/classes.py similarity index 77% rename from functions/lego_classes.py rename to functions/classes.py index 219614f5f18236333c67247b75d3f1e06e19e085..8e9a80354cca17dfca843cd0ad60c53f44e0e1ad 100644 --- a/functions/lego_classes.py +++ b/functions/classes.py @@ -1,11 +1,10 @@ -''' +""" 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 Any, Union, Literal, TypedDict, TypeVar, Type, List, Optional, Dict +from typing import List, Dict import json @@ -15,7 +14,8 @@ import json # - Gute String Darstellung # - Minimalbeispiel für KPIs # - Export als GraphViz - +# - Erlaube Listen bei add_component und add_assembly ? +# - Zukunft: Erlaube Clone bei Assembly (jede Component muss durch Klon ersetzt werden) class ComponentCategory(Enum): BATTERY = auto() @@ -34,8 +34,17 @@ class AggregationLayer(Enum): 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: + 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 @@ -48,8 +57,16 @@ class LegoComponent: self.properties: dict = properties def clone(self) -> LegoComponent: - clone = LegoComponent(self.name, self.category, self.lego_id, self.cost, self.mass, self.delivery_time, - self.layer, **self.properties) + 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): @@ -61,7 +78,17 @@ class LegoComponent: return current_assembly def to_dict(self) -> Dict: - ATTRIBUTES = ["uuid", "name", "category", "lego_id", "cost", "mass", "delivery_time", "layer", "properties"] + ATTRIBUTES = [ + "uuid", + "name", + "category", + "lego_id", + "cost", + "mass", + "delivery_time", + "layer", + "properties", + ] dict_ = {} # store attributes for attr in ATTRIBUTES: @@ -96,26 +123,34 @@ class LegoAssembly: def add_component(self, component: LegoComponent) -> None: if not isinstance(component, LegoComponent): - raise TypeError(f"Argument should be of type {LegoComponent.__name__}, " - f"got {type(component).__name__} instead.") + 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}.") + 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) -> None: if not isinstance(assembly, LegoAssembly): - raise TypeError(f"Argument should be of type {LegoAssembly.__name__}, " - f"got {type(assembly).__name__} instead.") + 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}.") + 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} + return {"components": self.components, "assemblies": self.assemblies} def get_component_list(self, max_depth: int = -1) -> List[LegoComponent]: component_list = [] diff --git a/main_kpi.py b/main_kpi.py index 9322121ae30d376f5b393b548493aea20f4608a4..c4748e5217c3c9dbef6f418e439387dbdc7aa319 100644 --- a/main_kpi.py +++ b/main_kpi.py @@ -1,11 +1,13 @@ -''' +""" FAIR Quality KPIs -''' +""" -def main (): + +def main(): print("Hello ;-)") pass + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/test-legoclasses.ipynb b/test-classes.ipynb similarity index 97% rename from test-legoclasses.ipynb rename to test-classes.ipynb index ba9d072fafc42df9e155fa9ef0bd4d3c0a3c87c9..95e0adffc7290f331ce592b7f3cf965bf5adce53 100644 --- a/test-legoclasses.ipynb +++ b/test-classes.ipynb @@ -7,8 +7,8 @@ "outputs": [], "source": [ "# import functions.lego_classes as lego_classes\n", - "from functions.lego_classes import LegoItem\n", - "from functions.lego_classes import LegoComponent" + "from functions.classes import LegoItem\n", + "from functions.classes import LegoComponent" ] }, { diff --git a/test-classes.py b/test-classes.py new file mode 100644 index 0000000000000000000000000000000000000000..0fbc4bee81165ba2b19eb4cf2522e16149c41eb0 --- /dev/null +++ b/test-classes.py @@ -0,0 +1,136 @@ +# import standard libraries +import json +import pprint + +# import classes from the classes module in functions package +from functions.classes import LegoComponent +from functions.classes import LegoAssembly +from functions.classes import ComponentCategory +from functions.classes import AggregationLayer +from functions.classes import KPIEncoder +from functions.classes import print_assembly_tree + +from functions.calculation_rules import kpi_sum +# Test manually creating some assemblies and components + +battery = LegoComponent("nice battery", ComponentCategory.BATTERY, "bat42", 1, 2, 3) +motor = LegoComponent("motor goes brrr", ComponentCategory.MOTOR, "motor", 1, 2, 3) +wheel = LegoComponent("much round wheel", ComponentCategory.WHEEL, "round", 1, 2, 3) + + +# Create subassemblies and combine to assembly (currently no parts present for this) +chassis = LegoAssembly("Chassis", AggregationLayer.ASSEMBLY) +door1 = LegoAssembly("Door 1", AggregationLayer.SUBASSEMBLY) +door2 = LegoAssembly("Door 2", AggregationLayer.SUBASSEMBLY) +chassis.add_assembly(door1) +chassis.add_assembly(door2) +chassis.properties + +engine = LegoAssembly("Engine", AggregationLayer.ASSEMBLY) +# Showcase cloning - one motor for each axis? +engine.add_component(motor.clone()) + + +fuel_tank = LegoAssembly("Fuel Tank", AggregationLayer.ASSEMBLY) +fuel_tank.add_component(battery.clone()) + + +wheels = LegoAssembly("Wheels", AggregationLayer.ASSEMBLY) +for _ in range(4): + wheels.add_component(wheel.clone()) + + +# Create and assemble the system level car +car = LegoAssembly("Car", AggregationLayer.SYSTEM) +car.add_assembly(chassis) +car.add_assembly(engine) +car.add_assembly(fuel_tank) +car.add_assembly(wheels) + + +## Printing + +print_assembly_tree(car) + +# Dump to json file +with open("car.json", "w") as fp: + json.dump(car.to_dict(), fp, cls=KPIEncoder) + +pprint.pprint(car.to_dict()) +pass + + +## Create components from datasheet +# Need to run the script from datasheets folder first +# Test loading dicts from datasheet jsons + +with open('datasheets/Achsen.json') as json_file: + Achsen = json.load(json_file) + +print((Achsen["Achse 5 studs"])) + +# This is hard to read, prettier please! + +pprint.pprint(Achsen) + +# Okay I'll use the 5 stud one +Achse5 = Achsen["Achse 5 studs"] # I'd prefer to call it via "Designnummer" +print(Achsen["Achse 5 studs"]["Designnummer"]) + +with open('datasheets/Räder.json') as json_file: + Raeder = json.load(json_file) +# Get me the expensive one +Radpreis = [] +for key in Raeder: # a list would work easier here than a dict + Radpreis.append(Raeder[key]["Preis [Euro]"]) +Radpreis = max(Radpreis) +for key in Raeder: + if Raeder[key]["Preis [Euro]"] == Radpreis: + TeuresRad=Raeder[key] + +# Also need a frame +with open('datasheets/Gestell.json') as json_file: + Gestelle = json.load(json_file) +Gestellbeschreibung = "Technic, Brick 1 x 8 with Holes" +Gestell = Gestelle[Gestellbeschreibung] + +# Create Components from these items +# LegoComponent(name: str, category: ComponentCategory, lego_id: str, cost: float, +# mass: float, delivery_time: int, layer: AggregationLayer = AggregationLayer.COMPONENT, **properties) +scooterframe = LegoComponent("running board", ComponentCategory.FRAME, + Gestell["Designnummer"], + Gestell["Preis [Euro]"], Gestell["Gewicht [g]"], + 0) + +scooterwheel = LegoComponent("Vorderrad", ComponentCategory.WHEEL, + TeuresRad["Designnummer"], + TeuresRad["Preis [Euro]"], TeuresRad["Gewicht [g]"], + 0) +# Cloning is necessary because each lego object gets a unique id so you can't use the same part twice +scooterwheel2 = scooterwheel.clone() +scooterwheel2.name = "Hinterrad" +print([scooterwheel.uuid, scooterwheel2.uuid]) + + +# Lets Assembly, first the Assembly object, directly add the frame? +scooter = LegoAssembly("my first scooter", AggregationLayer.SYSTEM) +scooter.add_component(scooterframe) # only one per call for now +scooter.add_component(scooterwheel) +scooter.add_component(scooterwheel2) +# <Assembly>.add_assembly() works in the same way + +# Look at our work: +print(scooter) +print(scooter.children()) + +## First KPI mass should be a sum +listofmasses= [] +for c in scooter.components: + listofmasses.append(c.mass) +for a in scooter.assemblies: + # only checking one layer deep here + listofmasses.append(a.mass) +print(listofmasses) +print(kpi_sum(listofmasses)) + +print("theend") \ No newline at end of file diff --git a/test-legoclasses.py b/test-legoclasses.py deleted file mode 100644 index 54f2900de7510e2b629174640ee185ece6e28651..0000000000000000000000000000000000000000 --- a/test-legoclasses.py +++ /dev/null @@ -1,42 +0,0 @@ - -# import functions.lego_classes as lego_classes -from functions.lego_classes import * -import json -import pprint -# Test manually creating some item and components - -battery = LegoComponent("nice battery", ComponentCategory.BATTERY, "bat42", 1, 2, 3) -motor = LegoComponent("motor goes brrr", ComponentCategory.MOTOR, "motor", 1, 2, 3) -wheel = LegoComponent("much round wheel", ComponentCategory.WHEEL, "round", 1, 2, 3) - -car = LegoAssembly("Car", AggregationLayer.SYSTEM) - -chassis = LegoAssembly("Chassis", AggregationLayer.ASSEMBLY) -door1 = LegoAssembly("Door 1", AggregationLayer.SUBASSEMBLY) -door2 = LegoAssembly("Door 2", AggregationLayer.SUBASSEMBLY) -chassis.add_assembly(door1) -chassis.add_assembly(door2) - -engine = LegoAssembly("Engine", AggregationLayer.ASSEMBLY) -engine.add_component(motor.clone()) - -fuel_tank = LegoAssembly("Fuel Tank", AggregationLayer.ASSEMBLY) -fuel_tank.add_component(battery.clone()) - -wheels = LegoAssembly("Wheels", AggregationLayer.ASSEMBLY) -for _ in range(4): - wheels.add_component(wheel.clone()) - -car.add_assembly(chassis) -car.add_assembly(engine) -car.add_assembly(fuel_tank) -car.add_assembly(wheels) - -print_assembly_tree(car) - -# Dump to json file -with open("car.json", "w") as fp: - json.dump(car.to_dict(), fp, cls=KPIEncoder) - -pprint.pprint(car.to_dict()) -pass