From b15e4bf646083d16c4f88395d0ace69a9bded514 Mon Sep 17 00:00:00 2001 From: "Hock, Martin" <martin.hock@fst.tu-darmstadt.de> Date: Mon, 27 Feb 2023 07:08:23 +0100 Subject: [PATCH] Add creation items from datasheets and simple kpi calculation --- datasheets/create_json_from_excel.py | 2 + functions/calculation_rules.py | 17 ++++-- functions/classes.py | 72 ++++++++++++++++------ test-classes.py | 91 +++++++++++++++++++++++++++- 4 files changed, 157 insertions(+), 25 deletions(-) diff --git a/datasheets/create_json_from_excel.py b/datasheets/create_json_from_excel.py index 4cdd8cf..2626950 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 diff --git a/functions/calculation_rules.py b/functions/calculation_rules.py index e9d20f1..7fa7396 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/classes.py b/functions/classes.py index f8d81c6..8e9a803 100644 --- a/functions/classes.py +++ b/functions/classes.py @@ -1,10 +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 @@ -14,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() @@ -33,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 @@ -47,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): @@ -60,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: @@ -95,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/test-classes.py b/test-classes.py index d1282ed..0fbc4be 100644 --- a/test-classes.py +++ b/test-classes.py @@ -1,6 +1,7 @@ # 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 @@ -9,22 +10,24 @@ from functions.classes import AggregationLayer from functions.classes import KPIEncoder from functions.classes import print_assembly_tree - -# Test manually creating some item and components +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) -car = LegoAssembly("Car", AggregationLayer.SYSTEM) +# 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()) @@ -36,11 +39,17 @@ 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 @@ -49,3 +58,79 @@ with open("car.json", "w") as fp: 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 -- GitLab