Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • philip.mahouttchi-hosseini/quality-kpi-final
  • boris.dasgupta/quality-kpi
  • hani.husseini/quality-kpi
  • tolga.guelec/quality-kpi
  • eva.schlott/quality-kpi
  • artashes.manukyan/quality-kpi
  • reinhart.tjahjadi/quality-kpi-reinhart-jason-tjahjadi
  • sotiris.giovannis/quality-kpi-giovanns
  • luis.gleich/quality-kpi
  • julius.damm/quality-kpi
  • gaganpreet.singh/quality-kpi
  • felix.fassler/aufgabe-1-felixf
  • benjamin.gross/quality-kpi
  • yixing.tao/quality-kpi-2024
  • louis.randa/quality-kpi
  • konstantinos.boiadjiev/quality-kpi
  • ammon.wuendisch/quality-kpi
  • tarish.kalra/quality-kpi
  • constantin.messingfeld/quality-kpi
  • manuel.kaster/quality-kpi
  • rafi.noordin/quality-kpi-rafinoordin
  • fst-tuda/public/lehre/quality-kpi
  • robert.schaefer1/quality-kpi
  • luca_tobias.nuecker/quality-kpi
  • calvin.weide/quality-kpi
  • maximilian.gross1/quality-kpi
  • christoph.froehlich/quality-kpi
  • young-min.kong/quality-kpi
  • thomas.gruber/quality-kpi
  • dennis.dibbern/quality-kpi
  • luca.sommer/quality-kpi
  • tushar.sharma/quality-kpi
  • philipp.schmidt2/quality-kpi
  • jakob.knoblach/quality-kpi
  • diogo.fernandes_costa/quality-kpi
  • zidane.buermann/quality-kpi
  • elias.rausch/quality-kpi
  • vincent.jestaedt/quality-kpi
  • santiago.ramirez_saldana/quality-kpi
  • jan.wech/quality-kpi
  • noah.michel1/lerneinheit-1-noah-michel
  • bastian.bickel/quality-kpi
  • linus.schuette/quality-kpi
  • henrik.buechs/quality-kpi
  • florian.weiss/quality-kpi
  • julie.kickstein/quality-kpi
  • paul.zuendel/quality-kpi
  • valentin.kaemmerer/quality-kpi
  • erwin.durasow/quality-kpi
  • david.buening/quality-kpi
  • keanu.engel/engel-keanu-auto
  • leo.mensler/quality-kpi
  • tahsin.ahmad/quality-kpi
  • moritz.roth/moritz-roth
  • jonathan.kollmann/quality-kpi
  • noah.waltmann/quality-kpi
  • daniel.burgos/quality-kpi
  • nilay.kuslugil/quality-kpi
  • yvan.ngoune/quality-kpi
  • eric.hahn/quality-kpi
  • andy.zheng/quality-kpi
  • junghyun.seo/quality-kpi
  • fabian.roesch1/quality-kpi
  • ole.quiring/quality-kpi
  • jonas.dietrich/quality-kpi
  • marius.stumpe/quality-kpi
  • maurizio.fell/quality-kpi
  • harry.kristanto/quality-kpi-prakdig-hk
  • paul.bobrinskoy/quality-kpi
  • vladimir.pascari/quality-kpi-vladimir-pascari
  • paul_konrad.braun/quality-kpi
  • leon.dawkins/quality-kpi
  • noel.schwibus/quality-kpi
  • bianca.beer/quality-kpi
  • pascal.pohlenz/quality-kpi
  • martin.cao/quality-kpi
  • malte.kramp/quality-kpi
  • jannik.hoffmann/quality-kpi
  • ishwor.kapri/quality-kpi
  • dhiraj.baral/quality-kpi
  • tobias.erdmann/quality-kpi
  • naveen.mohan/quality-kpi
  • filip.fetzer/quality-kpi
  • jakob.koch/quality-kpi
  • sascha.lamm/quality-kpi
  • lilly.laubenheimer/quality-kpi
  • amen.bouzouraa/quality-kpi
  • louis.senff/quality-kpi
  • francisco.flores/quality-kpi
  • benit.iberhysaj/quality-kpi
  • daniel.vratnjan/quality-kpi
  • henri.sprickmann/quality-kpi
  • tim.ostendorf/quality-kpi
  • maximilian.kopp1/lerneinheit-i-quality-kpi
  • zineb.karri/quality-kpi
  • bryan.lauren/quality-kpi
  • leon.herz/quality-kpi
  • zhibo.zhao/quality-kpi
  • samuel.neidert/quality-kpi
  • jiacheng.wang/quality-kpi
  • lucas.czarnecki/quality-kpi
  • lukas.ripp/quality-kpi
  • nikolai.brueckner/quality-kpi-nikolai-brueckner
  • niklas.ulmer/quality-kpi
  • lea_sophie.heering/quality-kpi
  • julia.kipp/quality-kpi
  • andres.rivera/quality-kpi
  • felix.meyners/quality-kpi
  • paul.sipp/quality-kpi
  • pascal.grym/quality-kpi
  • simon.peters/quality-kpi
  • justus.naewie/quality-kpi
  • alicia.sachs/quality-kpi
  • pascal.schraut/quality-kpi
  • jonas.dissinger/quality-kpi
  • mattheo.beyer/quality-kpi
  • adonay.johannes/quality-kpi
  • konrad.winkhaus/winkhaus-konrad-2809351-64-le-1
  • miko.boml/quality-kpi
  • bob.putz/quality-kpi
  • buesra.akkaya/quality-kpi
  • anton.littmann/quality-kpi
  • julian.volgmann/quality-kpi
  • soner.elbudak/quality-kpi
  • ilhan_mert.dumlupinar/quality-kpi
  • ibrahim.alsaleh/quality-kpi
  • amina.anizi/quality-kpi
  • Philipp.List/quality-kpi
  • greta.kirchhoff/quality-kpi
  • rowie.samuel/quality-kpi
130 results
Show changes
Commits on Source (48)
Showing
with 741 additions and 880 deletions
__pycache__/
\ No newline at end of file
# This file is a template, and might need editing before it works on your project.
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Python.gitlab-ci.yml
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: python:latest
# Change pip's cache directory to be inside the project directory since we can
# only cache local items.
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
# Pip's cache doesn't store the python packages
# https://pip.pypa.io/en/stable/topics/caching/
#
# If you want to also cache the installed packages, you have to install
# them in a virtualenv and cache it as well.
cache:
paths:
- .cache/pip
- venv/
before_script:
- python --version # For debugging
- pip install virtualenv
- virtualenv venv
- source venv/bin/activate
- which python
test:
tags:
- env:docker
script:
#- python setup.py test
- pip install flake8 flake8-nb # you can also use tox
- pwd
- ls -lh
- flake8 --max-line-length 88 ./functions/*.py
- flake8-nb --max-line-length 88 ausarbeitung.ipynb
#run:
#tags:
#- env:docker
#script:
#- pip install -r requirements.txt
#- python trial_json.py
# an alternative approach is to install and run:
# - pip install dist/*
# run the command here
# artifacts:
# paths:
# - dist/*.whl
#pages:
# tags:
# - env:docker
# script:
# - pip install sphinx sphinx-rtd-theme
# - cd doc
# - make html
# - mv build/html/ ../public/
# artifacts:
# paths:
# - public
# rules:
# - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
0
0 Name: New Model.ldr
0 Author:
1 71 180 -48 0 1 0 0 0 1 0 0 0 1 56904.dat
1 0 180 -48 0 1 0 0 0 1 0 0 0 1 30699.dat
1 4 260 -57.8657 -50 1 0 0 0 1 0 0 0 1 2730.dat
1 71 180 -48 -30 0 0 1 0 1 0 -1 0 0 32073.dat
1 47 180 -48 -30 1 0 0 0 1 0 0 0 1 3713.dat
1 47 180 -48 -70 1 0 0 0 1 0 0 0 1 3713.dat
1 71 340 -48 0 1 0 0 0 1 0 0 0 1 56904.dat
1 0 340 -48 0 1 0 0 0 1 0 0 0 1 30699.dat
1 71 340 -48 -30 0 0 1 0 1 0 -1 0 0 32073.dat
1 47 340 -48 -30 1 0 0 0 1 0 0 0 1 3713.dat
1 47 340 -48 -70 1 0 0 0 1 0 0 0 1 3713.dat
1 47 220 -128 -70 1 0 0 0 0 1 0 -1 0 40490.dat
1 47 220 -208 -70 0 0 1 0 1 0 -1 0 0 32073.dat
1 47 220 -208 -25 -1 0 0 0 1 0 0 0 -1 32123.dat
1 47 220 -208 -35 -1 0 0 0 1 0 0 0 -1 32123.dat
1 47 220 -208 -45 -1 0 0 0 1 0 0 0 -1 32123.dat
1 47 220 -208 -55 -1 0 0 0 1 0 0 0 -1 32123.dat
1 47 220 -208 -85 -1 0 0 0 1 0 0 0 -1 32123.dat
1 47 220 -208 -95 -1 0 0 0 1 0 0 0 -1 32123.dat
1 47 220 -208 -105 -1 0 0 0 1 0 0 0 -1 32123.dat
1 47 220 -208 -115 -1 0 0 0 1 0 0 0 -1 32123.dat
1 47 220 -48 -60 0 0 1 0 -1 0 1 0 0 3673.dat
File added
# Lerneinheit FAIRe Qualitäts-KPIs # Lerneinheit FAIRe Qualitäts-KPIs
## Einführung ## Einführung
Siehe Skript und Aufgabenstellung in [moodle](https://moodle.tu-darmstadt.de/course/view.php?id=36368&section=3#tabs-tree-start).
## Materialien ## Materialien
In diesem GitLab Repo finden Sie:
## Anwendung - Package functions (`functions/`): Beinhaltet die Module `classes` und `calculation_rules`
- Modul classes (`functions/classes.py`): Werkzeuge zum Aufbau und zur Bearbeitung der LEGO-Konstruktionen
- Modul calculation_rules (`functions/calculation_rules.py`): Funktionen zum Berechnen der FAIRen
Qualitäts-KPIs
- Python-Hilfsdatei (`functions/__init__.py`): Notwendige Datei für die Erzeugung eines Python-Pakets
- Datenblätter (`datasheets/`): Datenblätter ausgewählter LEGO-Komponenten im JSON-Format
- Teilebibliothek (`LeoCAD/library.bin`): Teilebibliothek für das Programm LeoCAD
- Abbildungsordner (`figures`): Ordner zum Ablegen der LeoCAD-Screenshots
- Beispiel-Notebook (`minimalbeispiel.ipynb`): Einführendes Beispiel zur Verwendung der Werkzeuge
- Ausarbeitungs-Notebook (`ausarbeitung.ipynb`): Vorlage zur Bearbeitung der Aufgaben und Abgabe
## Ausarbeitung ## Ausarbeitung
Die Ausarbeitung erfolgt im Notebook `ausarbeitung.ipynb`. In diesem ist bereits eine Gliederung vorgegeben. Die Ausarbeitung erfolgt im Notebook `ausarbeitung.ipynb`. In diesem ist bereits eine Gliederung vorgegeben.
## Abgabe ## Abgabe
Die Abgabe erfolgt über moodle. Die Abgabe erfolgt über [moodle](https://moodle.tu-darmstadt.de/mod/assign/view.php?id=1249192). Committen und pushen Sie zunächst Ihre Änderungen auf GitLab und laden Sie von dort Ihr gesamtes Repo als .zip-Datei herunter (ein direkter Download vom JupyterHub ist leider nicht möglich). Benennen Sie die .zip-Datei nach dem folgenden Schema:
<p style="text-align: center;"> &lt;Nachname&gt;_&lt;Vorname&gt;_&lt;MATR-NR&gt;_&lt;GRUPPEN-NR&gt;_le_1.zip</p>
Abgaben, die diese Namenskonvention nicht erfüllen, können in der Bewertung nicht berücksichtigt werden.
Laden Sie diese .zip-Datei in moodle hoch. Insbesondere sollten vorhanden sein:
- Jupyter Notebook mit Erklärungen und Darstellungen von LeoCAD
- Python-Funktionen für die Berechnung der KPIs
- Die exportierte(n) JSON-Datei(en) des Fahrzeuges (inkl. der hinzugefügten KPIs)
\ No newline at end of file
%% Cell type:markdown id:c9328cd1 tags: %% Cell type:markdown id:c9328cd1 tags:
# FAIR Quality KPIs # FAIRe Qualitäts-KPIs
Name: Autor:
Datum: Datum:
%% Cell type:markdown id:2ee8746d tags: %% Cell type:markdown id:1c5d41dc tags:
## Einführung ## Forschungsfrage
FAIR Quality KPIs schaffen eine transparente und nachvollziehbare Entscheidungsgrundlage. In dieser Lerneinheit sollen sie die Methodik erlernen, indem sie LEGO Autos zusammenbauen, deren Quality KPIs bestimmen und miteinander vergleichen. Formulieren Sie eine oder mehrere Forschungsfragen zu der Lerneinheit:
### Werkzeug für den Zusammenbau %% Cell type:markdown id:14e306b5 tags:
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.
### Berechnung der Quality KPIs
KPIs (Key Performance Indikatoren) sind Kenngrößen des Systems. Sie werden über Berechnungsvorschriften bestimmt. Diese Berechnungsvorschriften sind von Ihnen als python-Funktionen im Modul `calculation_rules` zu implementieren.
### Datenblätter %% Cell type:markdown id:de070039 tags:
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 tags:
## Eigene Module und Minimalbeispiel
Für die Ausarbeitung nutzen wir zwei eigene Module (Python-Dateien), die im Ordner `functions` gespeichert sind.
Das Modul `calculation_rules` erweitern Sie während der Ausarbeitung. Um die Änderungen zu nutzen, müssen Sie das Notebook neu starten.
Im Modul `classes` befinden sich die komplette Klassen und Funktionen zur Verwendung.
Mit einem Minimalbeispiel wird Ihnen gezeigt, wie sie die Module nutzen. ## Erstes Fahrzeug
%% Cell type:markdown id:6d3310be tags: %% Cell type:markdown id:531d890a tags:
### Modul classes ### Versuchsziel
Enthält `LegoComponent, LegoAssembly, AggregationLayer, KPIEncoder` und die Funktion `print_assembly_tree`. Formulieren Sie ein Versuchsziel für diesen ersten Versuch:
`LegoComponent` bildet einzelne Komponenten ab, während `LegoAssembly` zusammengesetzte Aggregationsebenen abdeckt, also Bauteil, Baugruppe und System. Zur Unterscheidung dient die Klasse Aggregationlayer, diese ist für `LegoComponent` immer `Component` (Komponente), muss für `LegoAssembly` entsprechend auf `SYSTEM`(System) , `ASSEMBLY`(Baugruppe) oder `SUBASSEMBLY`(Bauteil) gesetzt werden.
Wir bauen aus Achse, Rahmen und Reifen einen Tretroller zusammen. %% Cell type:markdown id:30ddd8dc tags:
%% Cell type:code id:b2778dee tags:
``` python %% Cell type:markdown id:50dd9ff3 tags:
# import modules
import json
import pprint
from functions import calculation_rules
# Importing all modules one by one to provide an overview ### Versuchsaufbau
# The next commented line would provide the same result in one line
# from functions.classes import *
from functions.classes import LegoComponent
from functions.classes import LegoAssembly
from functions.classes import AggregationLayer
from functions.classes import KPIEncoder
from functions.classes import print_assembly_tree
# When you are writing code yourself later, you might want to copy Bauen Sie ein erstes Fahrzeug aus den gegebenen LEGO-Teilen in der CAD-Software LeoCAD auf.
# these imports to avoid rerunning the full notebook after restart
```
%% Cell type:code id:0b1f9aff tags: Hierbei gelten die folgenden Rahmenbedingungen:
- Das Fahrzeug muss aus Baugruppen, Bauteilen und Komponenten bestehen.
- Es muss mindestens vier Räder besitzen
- Es muss sich durch den elektrischen Antrieb fortbewegen können.
- Die Verwendung eines Getriebes zwischen Motor und Antriebsachse(n) ist verpflichtend.
- Die Farbe von mindestens einem Teil soll sich von der in LeoCAD hinterlegten Standardfarbe unterscheiden.
- Es sind nur die LEGO-Teile zu verwenden, welche sich in den JSON-Dateien bzw. in
der zur Verfügung gestellten Teilebibliothek befinden.
``` python %% Cell type:markdown id:0ebba2d8 tags:
# Create the wheels and axles as single components first
# Look up the specific item you want from the provided json files,
# we can load the full file into a dict
with open("datasheets/axles.json") as json_file:
axles = json.load(json_file)
# Pick a specific axle via its 'item number'
print(axles["32073"])
# Create the component with the dict: Fügen Sie eine Abbildung des fertigen Autos in LeoCAD hinzu (*Hinweise: Ein Bild lässt sich mit \!\[Bildbeschreibung](/Pfad/zum/Bild) hinzufügen. Achten Sie darauf, das Bild später auch in Git hinzuzufügen*):
front_axle = LegoComponent("front axle", axles["32073"])
# Both label and the data dict are optional parameters.
# You can view, add or edit the properties just any dict:
print(front_axle.properties["label"])
front_axle.properties["color"] = "grey"
%% Cell type:markdown id:935c200c tags:
# Create the second axle
back_axle = LegoComponent()
back_axle.properties["label"] = "back axle"
back_axle.properties.update(axles["32073"])
# Do not use = here, otherwise you'd overwrite existing properties.
# Instead use update to have all entries added to properties
back_axle.properties["color"] = "grey"
# Viewing dicts in one line is not easy to read,
# a better output comes with pretty print (pprint):
pprint.pprint(back_axle.properties)
```
%% Cell type:code id:b4a8e8c8 tags: %% Cell type:markdown id:05a8eb21 tags:
``` python Beschreiben Sie kurz und präzise Ihr Fahrzeug:
# Now wheels
with open("datasheets/wheels.json") as json_file:
wheels = json.load(json_file)
# Adding the color here already as dict, and the surface as key-value argument.
# Multiple of both parameters are supported, but only in this order.
# front_wheel = LegoComponent(
# 'front wheel', wheels['2903'], {'color':'yellow'},winter='true')
front_wheel = LegoComponent( %% Cell type:markdown id:df7f1d01 tags:
"front wheel", wheels["2903"], surface="rough", paint="glossy"
)
pprint.pprint(front_wheel.properties)
# We included a clone function to both Lego classes, so you can easily
# create duplicate objects. Passing the new label is optional
back_wheel = front_wheel.clone("back wheel")
```
%% Cell type:code id:25bd06c5 tags: %% Cell type:markdown id:e622f83b tags:
``` python Bauen Sie das Fahrzeug nun in unserem Software-Framework zusammen. Instanziieren Sie die LEGO-Teile und lesen Sie dabei auch die Eigenschaften ein. Ergänzen Sie zusätzliche Eigenschaften, wie z.B. die Farbe. Referenzieren Sie die Teile aufeinander und erstellen Sie auf diese Weise sinnvolle Bauteile, Baugruppen und das
# Create subassemblies and add the wheels and axles Gesamtsystem. Nutzen Sie die hierfür bereitgestellten Klassendefinitionen und Methoden. Achten Sie auf eine gute Code-Dokumentation. Sie können sich für die Bearbeitung an den zur Verfügung gestellten Code-Zellen orientieren, können hiervon jedoch auch abweichen.
# The AggregationLayer must be used, passing the label *Hinweise: Achten Sie auf die Unterschiede zum Minimalbeispiel. Eine direkte Kopie ist nicht möglich. Achten Sie außerdem darauf, ein Teil (eindeutig identifiziert durch seine UUID) nicht an mehreren Stellen zu verbauen*
# or additional properties is optional
front_wheel_assembly = LegoAssembly(
AggregationLayer.SUBASSEMBLY,
"front wheel assembly",
assembly_method="stick together like lego blocks",
)
# Add LegoComponents to the LegoAssembly, single or as list
front_wheel_assembly.add([front_wheel, front_axle])
# You can access the components of an assembly like this:
print(front_wheel_assembly.components[1].properties["label"])
# Assemblies can be cloned as well (including their children), %% Cell type:code id:690da270 tags:
# but don't forget to adjust labels or you might be stuck with
# a 'front wheel' in your 'back wheel assembly'
# Stick together back wheel parts
back_wheel_assembly = LegoAssembly(
AggregationLayer.SUBASSEMBLY, "back wheel assembly"
)
back_wheel_assembly.add([back_wheel, back_axle])
```
%% Cell type:code id:2b6648e1 tags:
``` python ``` python
# Create frame component and assemble the system import json
import pprint
with open("datasheets/frame.json") as json_file: from functions import calculation_rules
frame = json.load(json_file) from functions.classes import *
scooter_frame = LegoComponent("scooter frame", frame["3702"], {"color": "red"})
# The scooter is our system level assembly, you can choose the AggregationLayer
# But the hiercarchy (SYSTEM > ASSEMBLY > SUBASSEMBLY) must be in order.
# Components can be added to all LegoAssembly objects
scooter = LegoAssembly(
AggregationLayer.SYSTEM,
"scooter",
manufacturer="FST",
comment="Faster! Harder! Scooter!",
)
# add frame and subassemblies
scooter.add([scooter_frame, front_wheel_assembly, back_wheel_assembly])
``` ```
%% Cell type:code id:71324895 tags: %% Cell type:code id:ccaf3043 tags:
``` python ``` python
# Look at the assembly # initialize components
# We can get all LegoComponents from this assembly.
# Without parameter 'max_depth' only direct children will be listed.
scooter.get_component_list(5)
``` ```
%% Cell type:markdown id:001f1c77 tags: %% Cell type:code id:36f981df tags:
### Modul calculation_rules
Um für unser System "Tretroller" ein KPI für das Gesamtgewicht zu erzeugen, wurde in `functions.calculation_rules` die Funktion `kpi_sum` definiert. Zusammen mit den Hilfsfunktionen der Klasse können wir nun das KPI Gewicht für das System hinzufügen. Die Massen der einzelnen Komponenten sind in den Datenblättern unter `mass [g]` enthalten.
%% Cell type:code id:7b60d0fb tags:
``` python ``` python
# test the import # set properties
calculation_rules.test_function()
``` ```
%% Cell type:code id:fe4edad6 tags: %% Cell type:code id:da2d141c tags:
``` python ``` python
# Add mass to assemblies # aggregate components
# List all components' mass
combined_weight = 0
for c in scooter.get_component_list(-1):
combined_weight += c.properties["mass [g]"]
print("Gesamtgewicht: ", combined_weight, "g")
# Add KPI to system
scooter.properties["mass [g]"] = combined_weight
# We can also add the mass to the subassemblies.
# children() returns a dict with a list of added
# components and assemblies.
for a in scooter.children()["assemblies"]:
a_mass = 0
for c in a.get_component_list(-1):
a_mass += c.properties["mass [g]"]
a.properties["mass [g]"] = a_mass
``` ```
%% Cell type:code id:c26b36c8 tags: %% Cell type:markdown id:c1fef7f0 tags:
``` python ### Analyse
scooter.get_component_list(-1) Bestimmen Sie die Qualität Ihres Fahrzeugs mittels KPIs.
``` 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:code id:4d56419f tags: %% Cell type:markdown id:d5f02096 tags:
``` python Beschreiben Sie den jeweiligen KPI und geben Sie seine Berechnungsvorschrift an:
# Look at the full assembly with KPI
# Print the full assembly with all levels %% Cell type:markdown id:a793bce8 tags:
print_assembly_tree(scooter)
```
%% Cell type:code id:b31416d3 tags: $$
a = \frac{b}{c} + d
$$
``` python %% Cell type:markdown id:9a21b4ac tags:
# Dump to json file with to_dict() and KPIEncoder
with open("scooter.json", "w") as fp:
json.dump(scooter.to_dict(), fp, cls=KPIEncoder)
# full view is too big for Juypter (try it)
# pprint.pprint(scooter.to_dict())
```
%% Cell type:markdown id:53793ae8 tags:
In dieser exportierten json-Datei ('scooter.json') sind die Werte maschinen- und menschenlesbar hinterlegt. %% Cell type:markdown id:e300a005 tags:
Zusammen mit der Berechnungsvorschrift in `calculation_rules` ist auch die Entstehung des KPI nachvollziehbar und wiederverwendbar dokumentiert und damit 'FAIR'.
%% Cell type:markdown id:92c77051 tags:
#### Zusätzliche Details %% Cell type:markdown id:2aad773a tags:
%% Cell type:code id:b91fed73 tags: Halten Sie die berechneten Werte für die KPIs im Notebook
fest:
``` python %% Cell type:code id:59eabafc tags:
# Each child knows its parent
first_child = scooter.children()["assemblies"][0]
print("Child:", first_child)
print("Parent:", first_child.parent)
# Also we can access the "top" parent
latest_child = first_child.children()["components"][0]
print("Top parent: ", latest_child.get_root_assembly())
# Each part created has a unique identifier (the long number in []) ``` python
# Don't try add the identical part again. # calculate the KPIs for your car
``` ```
%% Cell type:markdown id:de070039 tags: %% Cell type:code id:c774b381 tags:
## Aufgabe: Aufbau eines ersten Fahrzeugs ``` python
Bauen Sie ein erstes Fahrzeug aus den gegebenen LEGO-Teilen auf. # print your KPIs
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 tags: %% Cell type:markdown id:b89e8fb9 tags:
### Beschreibung des Fahrzeuges Exportieren Sie schließlich Ihr entworfenes Fahrzeug inklusive der entwickelten KPIs:
Beschreiben Sie kurz und präzise Ihr Fahrzeug.
%% Cell type:code id:ccaf3043 tags: %% Cell type:code id:d518275f tags:
``` python ``` python
# initialize componentens # export car and its properties
``` ```
%% Cell type:code id:1ea61422 tags: %% Cell type:markdown id:89c75440 tags:
``` python ## Zweites Fahrzeug
# read out properties from datasheets
```
%% Cell type:code id:36f981df tags: %% Cell type:markdown id:f8a2e1b0 tags:
``` python ### Versuchsziel
# set properties
```
%% Cell type:code id:24400d94 tags: %% Cell type:markdown id:80407e7f tags:
``` python Setzen Sie sich ein Ziel, welche Qualitätsdimensionen in einem zweiten Fahrzeug verbessert werden sollen und bauen
# export car and its properties Sie darauf aufbauend ein zweites Fahrzeug aus den gegebenen LEGO-Teilen auf.
``` Die Anforderungen an das Fahrzeug sind identisch zum ersten. Wählen Sie die Einzelteile und deren Zusammenspiel entsprechend Ihrer Zielstellung aus.
%% Cell type:markdown id:c1fef7f0 tags: %% Cell type:markdown id:f4c620ee tags:
## Aufgabe: Bestimmung der Qualität mittels KPIs Formulieren Sie ein Versuchsziel für diesen Versuch. Beschreiben Sie unter anderem, welche Verbesserung Sie vornehmen möchten:
Bestimmen Sie die Qualität Ihres Fahrzeugs mittels KPIs.
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 tags: %% Cell type:markdown id:e3dc7e29 tags:
### Beschreibung KPI
Beschreiben Sie den jeweiligen KPI und geben Sie seine Berechnungsvorschrift an.
$$
a = \frac{b}{c} + d
$$
%% Cell type:code id:59eabafc tags: %% Cell type:markdown id:73c454f2 tags:
``` python ### Versuchsaufbau
# calculate the KPIs for your car
```
%% Cell type:code id:c774b381 tags: %% Cell type:markdown id:2b381a60 tags:
``` python Fügen Sie eine Abbildung des fertigen Autos in LeoCAD hinzu:
# print your KPIs
```
%% Cell type:markdown id:89c75440 tags: %% Cell type:markdown id:2b6e7f12 tags:
## Aufgabe: Aufbau eines zweiten Fahrzeugs
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.
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:23c19009 tags:
%% Cell type:markdown id:3cef0828 tags: Beschreiben Sie kurz und präzise den Aufbau des zweiten Fahrzeugs:
### Beschreibung %% Cell type:markdown id:a08bf9cf tags:
Beschreiben Sie, welche Verbesserung Sie vornehmen.
...
%% Cell type:markdown id:73c454f2 tags:
### Aufbau des zweiten Fahrzeuges %% Cell type:markdown id:14011b6f tags:
Beschreiben Sie kurz und präzise den Aufbau des zweiten Fahrzeugs
%% Cell type:code id:ea18e735 tags: Erstellen Sie das Fahrzeug in unserem Softwareframework:
``` python %% Cell type:code id:c35de753 tags:
# build a second car
```
%% Cell type:code id:b3438fc4 tags:
``` python ``` python
# read out properties from datasheets # initialize components
``` ```
%% Cell type:code id:0b7336fb tags: %% Cell type:code id:0b7336fb tags:
``` python ``` python
# set properties # set properties
``` ```
%% Cell type:code id:446abbca tags: %% Cell type:code id:fb445ea0 tags:
``` python ``` python
# export car and its properties # aggregate components
``` ```
%% Cell type:markdown id:89e54480 tags: %% Cell type:markdown id:89e54480 tags:
### KPIs des zweiten Fahrzeuges ### Analyse
Bestimmen Sie die KPIs des zweiten Fahrzeuges Bestimmen Sie die KPIs des zweiten Fahrzeugs
%% Cell type:code id:762a1e93 tags: %% Cell type:code id:762a1e93 tags:
``` python ``` python
# calculate the KPIs for your car # calculate the KPIs for your car
``` ```
%% Cell type:code id:1ed67328 tags: %% Cell type:code id:1ed67328 tags:
``` python ``` python
# print your KPIs # print your KPIs
``` ```
%% Cell type:markdown id:0f11b370 tags:
Exportieren Sie Ihr Fahrzeug inklusive der KPIs:
%% Cell type:code id:05d9d6f7 tags:
``` python
# export car and its properties
```
%% Cell type:markdown id:e413cd84 tags: %% Cell type:markdown id:e413cd84 tags:
## Aufgabe: Darstellung und Vergleich der Ergebnisse ## Diskussion
Stellen Sie die Ergebnisse für beide Fahrzeuge übersichtlich dar. ### Ergebnisse
Die entwickelten KPIs müssen dargestellt und die Datengrundlage ersichtlich sein. Stellen Sie die entwickelten KPIs beider Fahrzeuge gegenüber und wählen Sie hierfür unter anderem eine geeignete
Eine geeignete grafische Darstellung für die Gegenüberstellung der Ergebnisse für beide Fahrzeuge ist zu wählen. grafische Darstellung. Stellen Sie dabei insbesondere sicher, dass die Datengrundlage ersichtlich ist. Halten Sie
auch die Plotbefehle im Notebook fest:
%% Cell type:code id:b0f93e22 tags: %% Cell type:code id:b0f93e22 tags:
``` python ``` python
# plot the data, save diagramm as svg-file # plot the data, save diagramm as svg-file
``` ```
%% Cell type:markdown id:62ff04b2 tags: %% Cell type:markdown id:6044de27 tags:
### Interpretation der Ergebnisse Interpretieren Sie Ihre Ergebnisse. Vergleichen Sie die KPIs Ihrer Autos. Konnten Sie Ihre gewünschte Verbesserung erzielen? Welche Schlüsse ziehen Sie aus den Ergebnissen für die Qualität der beiden
Interpretieren Sie ihre Ergebnisse. Vergleichen Sie die KPIs ihrer Autos. Konnten Sie ihre gewünschte Verbesserung erzielen? Fahrzeuge?
%% Cell type:markdown id:a0840fbf tags: %% Cell type:markdown id:0ca884b1 tags:
## Aufgabe: Exportieren Sie ihre Daten
Exportieren/ Speichern Sie alle relevaten Daten ab.
%% Cell type:code id:a4bdfc7a tags: %% Cell type:markdown id:4f117169 tags:
Diskutieren Sie, inwieweit Ihre entwickelten KPIs die im Skript erläuterten FAIR-Prinzipien erfüllen:
%% Cell type:markdown id:f8ed82d2 tags:
%% Cell type:markdown id:bfed164a tags:
## Fazit
%% Cell type:markdown id:5d440f87 tags:
Ziehen Sie ein persönliches Fazit. Was haben Sie Neues gelernt?
%% Cell type:markdown id:b4151784 tags:
``` python
# export all relevant data
```
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
"category":"axle", "category":"axle",
"price [Euro]":0.001, "price [Euro]":0.001,
"mass [g]":0.66, "mass [g]":0.66,
"environmental impact [kg CO2e /kg]":11.03,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32073#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32073",
"dimension [studs]":5 "dimension [studs]":5
}, },
"44294":{ "44294":{
...@@ -15,8 +16,9 @@ ...@@ -15,8 +16,9 @@
"category":"axle", "category":"axle",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":1.05, "mass [g]":1.05,
"environmental impact [kg CO2e /kg]":11.03,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=44294#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=44294",
"dimension [studs]":7 "dimension [studs]":7
}, },
"3707":{ "3707":{
...@@ -25,8 +27,9 @@ ...@@ -25,8 +27,9 @@
"category":"axle", "category":"axle",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":1.18, "mass [g]":1.18,
"environmental impact [kg CO2e /kg]":11.03,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3707#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3707",
"dimension [studs]":8 "dimension [studs]":8
}, },
"60485":{ "60485":{
...@@ -35,8 +38,9 @@ ...@@ -35,8 +38,9 @@
"category":"axle", "category":"axle",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":1.3, "mass [g]":1.3,
"environmental impact [kg CO2e /kg]":11.03,
"delivery time [days]":7, "delivery time [days]":7,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=60485#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=60485",
"dimension [studs]":9 "dimension [studs]":9
}, },
"3737":{ "3737":{
...@@ -45,8 +49,9 @@ ...@@ -45,8 +49,9 @@
"category":"axle", "category":"axle",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":1.49, "mass [g]":1.49,
"environmental impact [kg CO2e /kg]":11.03,
"delivery time [days]":7, "delivery time [days]":7,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3737#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3737",
"dimension [studs]":10 "dimension [studs]":10
}, },
"23948":{ "23948":{
...@@ -55,8 +60,9 @@ ...@@ -55,8 +60,9 @@
"category":"axle", "category":"axle",
"price [Euro]":0.15, "price [Euro]":0.15,
"mass [g]":1.65, "mass [g]":1.65,
"environmental impact [kg CO2e /kg]":11.03,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=23948#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=23948",
"dimension [studs]":11 "dimension [studs]":11
}, },
"3708":{ "3708":{
...@@ -65,8 +71,9 @@ ...@@ -65,8 +71,9 @@
"category":"axle", "category":"axle",
"price [Euro]":0.02, "price [Euro]":0.02,
"mass [g]":1.82, "mass [g]":1.82,
"environmental impact [kg CO2e /kg]":11.03,
"delivery time [days]":7, "delivery time [days]":7,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3708#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3708",
"dimension [studs]":12 "dimension [studs]":12
}, },
"50451":{ "50451":{
...@@ -75,8 +82,9 @@ ...@@ -75,8 +82,9 @@
"category":"axle", "category":"axle",
"price [Euro]":0.75, "price [Euro]":0.75,
"mass [g]":2.37, "mass [g]":2.37,
"environmental impact [kg CO2e /kg]":11.03,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=50451#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=50451",
"dimension [studs]":16 "dimension [studs]":16
} }
} }
\ No newline at end of file
{ {
"8878-1":{
"item number":"8878-1",
"item description":"Power Functions Rechargeable Battery Box",
"category":"battery",
"price [Euro]":55,
"mass [g]":83.94,
"delivery time [days]":8,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?S=8878-1#T=S&O={%22iconly%22:0}",
"output voltage [V]":7.4,
"dimensions [cm]":"15 x 15 x 3,5"
},
"8881-1":{ "8881-1":{
"item number":"8881-1", "item number":"8881-1",
"item description":"Power Functions Battery Box", "item description":"Power Functions Battery Box",
"category":"battery", "category":"battery",
"related items":"8882-1, 8883-1, 88003-1",
"price [Euro]":12, "price [Euro]":12,
"mass [g]":179.4, "mass [g]":179.4,
"environmental impact [kg CO2e /kg]":6.95,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?S=8881-1#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?S=8881-1",
"output voltage [V]":9.0, "output voltage [V]":9,
"dimensions [cm]":"8,8 x 6,3 x 3,2" "dimensions [cm]":"8,8 x 6,3 x 3,2"
},
"88000-1":{
"item number":"88000-1",
"item description":"Lego AAA Battery Box",
"category":"battery",
"price [Euro]":30,
"mass [g]":97.24,
"delivery time [days]":6,
"data source":"https:\/\/www.brickowl.de\/catalog\/lego-aaa-battery-box-set-88000",
"output voltage [V]":9.0,
"dimensions [cm]":"15,3 x 15,1 x 3,9"
},
"2847c01":{
"item number":"2847c01",
"item description":"Electric 9V Battery Box",
"category":"battery",
"price [Euro]":2,
"mass [g]":172.7,
"delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=2847c01&name=Electric%209V%20Battery%20Box%204%20x%2014%20x%204%20with%20Red%20Buttons%20and%20Contact%20Plate%20with%20Dark%20Gray%20Base%20(2846%20\/%202847c00)&category=%5BElectric,%20Battery%20Box%5D#T=S&O={%22iconly%22:0}",
"output voltage [V]":9.0,
"dimensions [cm]":"11,2 x 3,2 x 3,2"
},
"59510":{
"item number":59510,
"item description":"LEGO Power Functions Battery Box ",
"category":"battery",
"price [Euro]":18,
"mass [g]":165.3,
"delivery time [days]":3,
"data source":"https:\/\/www.brickowl.de\/catalog\/lego-power-functions-battery-box-with-beam-connectors-with-on-off-sticker-59510",
"output voltage [V]":9.0,
"dimensions [cm]":"8,8 x 6,4 x 3,2"
},
"84599":{
"item number":84599,
"item description":"Electric 9V Power Functions Battery Box (Rechargeable)",
"category":"battery",
"price [Euro]":120,
"mass [g]":82.0,
"delivery time [days]":8,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=84599&name=Electric%209V%20Battery%20Box%20Power%20Functions%20(Rechargeable)%20with%20Dark%20Bluish%20Gray%20Bottom&category=%5BElectric,%20Battery%20Box%5D#T=S&O={%22iconly%22:0}",
"output voltage [V]":9.0,
"dimensions [cm]":"15 x 15 x 3,5"
} }
} }
\ No newline at end of file
"""Create json metadata files from an excel file."""
import os
import sys
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
KEYWORD_TO_IGNORE_COLUMN = "ignore"
KEYWORD_TO_IGNORE_SHEET = "ignore"
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.
"""
def read_terminal_arguments() -> NamedTuple:
"""Read terminal arguments.
1. File to read from.
2. Name of the folder to store the json files.
3. Override the destination folder if existing, defaults False.
"""
terminal_args = namedtuple(
"terminal_args",
["source", "destination", "override"],
defaults=["test.xlsx", "test", True]
)
if len(sys.argv) == 1: # No arguments passed.
return terminal_args() # noqa
if sys.argv[1] in ["help", "--help", "-h", "?", "h"]:
print(HELP_TXT)
raise SystemExit
try:
arg_1 = sys.argv[1]
except IndexError:
raise SystemExit
try:
arg_2 = sys.argv[2]
except IndexError:
raise SystemExit
try:
arg_3 = sys.argv[3]
except IndexError:
arg_3 = False
terminal_args = terminal_args(
source=arg_1,
destination=arg_2,
override=bool(arg_3)
)
return terminal_args
def read_excel_file(path: str) -> Dict[str, pd.DataFrame]:
"""Read the sheets of the Excel file into multiple dataframes."""
if not os.path.exists(path):
raise FileNotFoundError(
f"No file found at '{path}'"
)
sheets_as_dataframes = {}
with pd.ExcelFile(path) as xls:
for sheet_name in xls.sheet_names:
if sheet_name.startswith(KEYWORD_TO_IGNORE_SHEET):
continue
excel_file = pd.read_excel(
xls,
sheet_name,
index_col=COLUMN_FOR_INDEX_IN_EXCEL
)
sheets_as_dataframes.update({sheet_name: excel_file})
return sheets_as_dataframes
def make_json(dataframes: Dict[str, pd.DataFrame]) -> Dict[str, str]:
"""Create the json strings from the excel data."""
json_files = {}
for sheet_name, dataframe in dataframes.items():
dataframe.dropna(inplace=True, axis=1)
dropped_columns = [column for column in dataframe.columns
if column.startswith(KEYWORD_TO_IGNORE_COLUMN)]
dataframe.drop(dropped_columns, axis=1, inplace=True,
errors="ignore")
print(sheet_name)
json_string = dataframe.to_json(orient="index", indent=4, force_ascii=False)
json_files.update({sheet_name: json_string})
return json_files
def save_json_files(folder_name: str, jsons_files: Dict[str, str],
override_files: bool) -> None:
"""Save the json strings to a '.json' file format."""
path_to_json_folder = os.path.join(
os.path.abspath(__file__),
os.pardir,
folder_name
)
path_to_json_folder = os.path.abspath(path_to_json_folder)
path_exists = os.path.exists(path_to_json_folder)
if path_exists and not override_files:
raise FileExistsError(
f"Folder: {path_to_json_folder} exists. Pass override argument "
f"to override the folder. See --help for more."
)
if not path_exists:
os.mkdir(path_to_json_folder)
for json_name, json_sting in jsons_files.items():
file_name = f"{json_name}.json"
file_path = os.path.join(path_to_json_folder, file_name)
with open(file_path, "w") as f:
f.write(json_sting)
def main():
"""The main function."""
# Get terminal arguments.
terminal_arguments = read_terminal_arguments()
# Read the Excel file.
path_to_file = os.path.abspath(os.path.join(
os.path.abspath(__file__),
os.pardir,
terminal_arguments.source)
)
excel_sheets_as_dataframes = read_excel_file(path_to_file)
excel_sheets_as_json = make_json(excel_sheets_as_dataframes)
save_json_files(terminal_arguments.destination, excel_sheets_as_json,
terminal_arguments.override)
if __name__ == "__main__":
t = main()
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
"category":"frame", "category":"frame",
"price [Euro]":2.19, "price [Euro]":2.19,
"mass [g]":12.96, "mass [g]":12.96,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=39790#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=39790",
"dimension [studs]":"11 x 15 x 1" "dimension [studs]":"11 x 15 x 1"
}, },
"32532":{ "32532":{
...@@ -15,8 +16,9 @@ ...@@ -15,8 +16,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.18, "price [Euro]":0.18,
"mass [g]":8.0, "mass [g]":8.0,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32532#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32532",
"dimension [studs]":"6 x 8 x 1" "dimension [studs]":"6 x 8 x 1"
}, },
"32531":{ "32531":{
...@@ -25,8 +27,9 @@ ...@@ -25,8 +27,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.1, "price [Euro]":0.1,
"mass [g]":5.0, "mass [g]":5.0,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32531&idColor=11#T=C&C=11", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32531",
"dimension [studs]":"4 x 6 x 1" "dimension [studs]":"4 x 6 x 1"
}, },
"3700":{ "3700":{
...@@ -35,8 +38,9 @@ ...@@ -35,8 +38,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":0.82, "mass [g]":0.82,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3700#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3700",
"dimension [studs]":"1 x 2" "dimension [studs]":"1 x 2"
}, },
"3701":{ "3701":{
...@@ -45,8 +49,9 @@ ...@@ -45,8 +49,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":1.46, "mass [g]":1.46,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3701#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3701",
"dimension [studs]":"1 x 4 x 1" "dimension [studs]":"1 x 4 x 1"
}, },
"3702":{ "3702":{
...@@ -55,8 +60,9 @@ ...@@ -55,8 +60,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":2.85, "mass [g]":2.85,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3702#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3702",
"dimension [studs]":"1 x 8 x 1" "dimension [studs]":"1 x 8 x 1"
}, },
"2730":{ "2730":{
...@@ -65,8 +71,9 @@ ...@@ -65,8 +71,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":3.67, "mass [g]":3.67,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=2730#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=2730",
"dimension [studs]":"1 x 10 x 1" "dimension [studs]":"1 x 10 x 1"
}, },
"3895":{ "3895":{
...@@ -75,8 +82,9 @@ ...@@ -75,8 +82,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.03, "price [Euro]":0.03,
"mass [g]":4.2, "mass [g]":4.2,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3895#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3895",
"dimension [studs]":"1 x 12 x 1" "dimension [studs]":"1 x 12 x 1"
}, },
"32018":{ "32018":{
...@@ -85,8 +93,9 @@ ...@@ -85,8 +93,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.03, "price [Euro]":0.03,
"mass [g]":4.92, "mass [g]":4.92,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32018#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32018",
"dimension [studs]":"1 x 14 x 1" "dimension [studs]":"1 x 14 x 1"
}, },
"3703":{ "3703":{
...@@ -95,8 +104,9 @@ ...@@ -95,8 +104,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.07, "price [Euro]":0.07,
"mass [g]":5.87, "mass [g]":5.87,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3703#T=C", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3703",
"dimension [studs]":"1 x 16 x 1" "dimension [studs]":"1 x 16 x 1"
}, },
"32524":{ "32524":{
...@@ -105,8 +115,9 @@ ...@@ -105,8 +115,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":1.79, "mass [g]":1.79,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32524&name=Technic,%20Liftarm%20Thick%201%20x%207&category=%5BTechnic,%20Liftarm%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32524",
"dimension [studs]":"1 x 7" "dimension [studs]":"1 x 7"
}, },
"40490":{ "40490":{
...@@ -115,18 +126,20 @@ ...@@ -115,18 +126,20 @@
"category":"frame", "category":"frame",
"price [Euro]":0.02, "price [Euro]":0.02,
"mass [g]":2.59, "mass [g]":2.59,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=40490&name=Technic,%20Liftarm%20Thick%201%20x%209&category=%5BTechnic,%20Liftarm%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=40490",
"dimension [studs]":"1 x 9" "dimension [studs]":"1 x 9"
}, },
"32525":{ "64290":{
"item number":32525, "item number":64290,
"item description":"Technic, Liftarm Thick 1 x 11", "item description":"Technic, Liftarm Thick 1 x 11",
"category":"frame", "category":"frame",
"price [Euro]":0.02, "price [Euro]":0.02,
"mass [g]":2.8, "mass [g]":2.8,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32525&name=Technic,%20Liftarm%20Thick%201%20x%2011&category=%5BTechnic,%20Liftarm%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32525",
"dimension [studs]":"1 x 11" "dimension [studs]":"1 x 11"
}, },
"41239":{ "41239":{
...@@ -135,8 +148,9 @@ ...@@ -135,8 +148,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.05, "price [Euro]":0.05,
"mass [g]":3.3, "mass [g]":3.3,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=41239&name=Technic,%20Liftarm%20Thick%201%20x%2013&category=%5BTechnic,%20Liftarm%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=41239",
"dimension [studs]":"1 x 13" "dimension [studs]":"1 x 13"
}, },
"32278":{ "32278":{
...@@ -145,8 +159,9 @@ ...@@ -145,8 +159,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.05, "price [Euro]":0.05,
"mass [g]":4.0, "mass [g]":4.0,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32278&name=Technic,%20Liftarm%20Thick%201%20x%2015&category=%5BTechnic,%20Liftarm%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32278",
"dimension [studs]":"1 x 15" "dimension [studs]":"1 x 15"
}, },
"3713":{ "3713":{
...@@ -155,18 +170,20 @@ ...@@ -155,18 +170,20 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":0.14, "mass [g]":0.14,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3713&name=Technic%20Bush&category=%5BTechnic%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3713",
"dimension [studs]":1 "dimension [studs]":1
}, },
"32123":{ "32123":{
"item number":32123, "item number":32123,
"item description":"Technic Bush 1\/2 Smooth", "item description":"Technic Bush 1/2 Smooth",
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":0.01, "mass [g]":0.01,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=4265c&name=Technic%20Bush%201\/2%20Smooth&category=%5BTechnic%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=4265c",
"dimension [studs]":1 "dimension [studs]":1
}, },
"3749":{ "3749":{
...@@ -175,48 +192,31 @@ ...@@ -175,48 +192,31 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":0.22, "mass [g]":0.22,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3749&name=Technic,%20Axle%20%201L%20with%20Pin%20without%20Friction%20Ridges&category=%5BTechnic,%20Axle%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3749",
"dimension [studs]":"0,75 x 2 x 0,55" "dimension [studs]":"0,75 x 2 x 0,55"
}, },
"6538":{
"item number":6538,
"item description":"Technic, Axle Connector 2L (Ridged Undetermined Type)",
"category":"frame",
"price [Euro]":0.03,
"mass [g]":0.4,
"delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=6538#T=C",
"dimension [studs]":"?"
},
"6536":{ "6536":{
"item number":6536, "item number":6536,
"item description":"Technic, Axle and Pin Connector Perpendicular", "item description":"Technic, Axle and Pin Connector Perpendicular",
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":0.39, "mass [g]":0.39,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":7, "delivery time [days]":7,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=6536&name=Technic,%20Axle%20and%20Pin%20Connector%20Perpendicular&category=%5BTechnic,%20Connector%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=6536",
"dimension [studs]":"1x 2 x 1" "dimension [studs]":"1x 2 x 1"
}, },
"32138":{
"item number":32138,
"item description":"Technic, Pin Double with Axle Hole",
"category":"frame",
"price [Euro]":0.01,
"mass [g]":0.96,
"delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32138&name=Technic,%20Pin%20Double%20with%20Axle%20Hole&category=%5BTechnic,%20Pin%5D#T=S&O={%22iconly%22:0}",
"dimension [studs]":"?"
},
"14720":{ "14720":{
"item number":14720, "item number":14720,
"item description":"Technic, Liftarm, Modified H-Shape Thick 3 x 5 Perpendicular", "item description":"Technic, Liftarm, Modified H-Shape Thick 3 x 5 Perpendicular",
"category":"frame", "category":"frame",
"price [Euro]":0.08, "price [Euro]":0.08,
"mass [g]":2.29, "mass [g]":2.29,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=14720&name=Technic,%20Liftarm,%20Modified%20H-Shape%20Thick%203%20x%205%20Perpendicular&category=%5BTechnic,%20Liftarm%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=14720",
"dimension [studs]":"3 x 5 x 1" "dimension [studs]":"3 x 5 x 1"
}, },
"48989":{ "48989":{
...@@ -225,8 +225,9 @@ ...@@ -225,8 +225,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":1.22, "mass [g]":1.22,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=48989&name=Technic,%20Pin%20Connector%20Perpendicular%203L%20with%204%20Pins&category=%5BTechnic,%20Connector%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=48989",
"dimension [studs]":"3 x 3 x 1" "dimension [studs]":"3 x 3 x 1"
}, },
"55615":{ "55615":{
...@@ -235,8 +236,9 @@ ...@@ -235,8 +236,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.1, "price [Euro]":0.1,
"mass [g]":1.9, "mass [g]":1.9,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=55615&name=Technic,%20Pin%20Connector%20Perpendicular%203%20x%203%20Bent%20with%204%20Pins&category=%5BTechnic,%20Connector%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=55615",
"dimension [studs]":"4 x 4 x 1" "dimension [studs]":"4 x 4 x 1"
}, },
"32556":{ "32556":{
...@@ -245,8 +247,9 @@ ...@@ -245,8 +247,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.1, "price [Euro]":0.1,
"mass [g]":0.25, "mass [g]":0.25,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32556&name=Technic,%20Pin%203L%20without%20Friction%20Ridges&category=%5BTechnic,%20Pin%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32556",
"dimension [studs]":1 "dimension [studs]":1
}, },
"3673":{ "3673":{
...@@ -255,8 +258,9 @@ ...@@ -255,8 +258,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":0.16, "mass [g]":0.16,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3673&name=Technic,%20Pin%20without%20Friction%20Ridges&category=%5BTechnic,%20Pin%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3673",
"dimension [studs]":1 "dimension [studs]":1
}, },
"32054":{ "32054":{
...@@ -265,8 +269,9 @@ ...@@ -265,8 +269,9 @@
"category":"frame", "category":"frame",
"price [Euro]":0.01, "price [Euro]":0.01,
"mass [g]":0.33, "mass [g]":0.33,
"environmental impact [kg CO2e /kg]":11.93,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32054&name=Technic,%20Pin%203L%20with%20Friction%20Ridges%20and%20Stop%20Bush&category=%5BTechnic,%20Pin%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32054",
"dimension [studs]":1 "dimension [studs]":1
} }
} }
\ No newline at end of file
{ {
"3647":{ "6589":{
"item number":3647, "item number":6589,
"item description":"Gear 8 Tooth", "item description":"Gear 12 Tooth Bevel",
"category":"gear", "category":"gear",
"price [Euro]":0.14, "price [Euro]":0.01,
"mass [g]":0.16, "mass [g]":0.29,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":12,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=6589"
},
"10928":{
"item number":10928,
"item description":"Gear 8 Tooth with Dual Face",
"category":"gear",
"price [Euro]":0.01,
"mass [g]":0.22,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":11, "delivery time [days]":11,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3647#T=C" "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=10928"
}, },
"94925":{ "94925":{
"item number":94925, "item number":94925,
...@@ -14,44 +25,39 @@ ...@@ -14,44 +25,39 @@
"category":"gear", "category":"gear",
"price [Euro]":0.2, "price [Euro]":0.2,
"mass [g]":0.7, "mass [g]":0.7,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":12, "delivery time [days]":12,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=94925#T=C" "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=94925"
}, },
"32269":{ "18575":{
"item number":32269, "item number":18575,
"item description":"Gear 20 Tooth", "item description":"Gear 20 Tooth Double Bevel",
"category":"gear", "category":"gear",
"price [Euro]":0.36, "price [Euro]":0.36,
"mass [g]":1.4, "mass [g]":1.4,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":13, "delivery time [days]":13,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32269#T=C" "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32269"
}, },
"3648":{ "24505":{
"item number":3648, "item number":24505,
"item description":"Gear 24 Tooth", "item description":"Gear 24 Tooth",
"category":"gear", "category":"gear",
"price [Euro]":0.32, "price [Euro]":0.32,
"mass [g]":1.17, "mass [g]":1.17,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":11, "delivery time [days]":11,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3648#T=C" "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3648"
},
"3650":{
"item number":3650,
"item description":"Gear 24 Tooth Crown",
"category":"gear",
"price [Euro]":0.09,
"mass [g]":1.03,
"delivery time [days]":13,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3650#T=C"
}, },
"3649":{ "34432":{
"item number":3649, "item number":34432,
"item description":"Gear 40 Tooth", "item description":"Gear 40 Tooth",
"category":"gear", "category":"gear",
"price [Euro]":0.81, "price [Euro]":0.81,
"mass [g]":3.76, "mass [g]":3.76,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":11, "delivery time [days]":11,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3649#T=C" "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3649"
}, },
"32498":{ "32498":{
"item number":32498, "item number":32498,
...@@ -59,8 +65,9 @@ ...@@ -59,8 +65,9 @@
"category":"gear", "category":"gear",
"price [Euro]":0.88, "price [Euro]":0.88,
"mass [g]":3.5, "mass [g]":3.5,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":12, "delivery time [days]":12,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=32498#T=C" "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=32498"
}, },
"6588":{ "6588":{
"item number":6588, "item number":6588,
...@@ -68,8 +75,9 @@ ...@@ -68,8 +75,9 @@
"category":"gear", "category":"gear",
"price [Euro]":1.63, "price [Euro]":1.63,
"mass [g]":4.5, "mass [g]":4.5,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":11, "delivery time [days]":11,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=6588#T=C" "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=6588#T=C"
}, },
"4716":{ "4716":{
"item number":4716, "item number":4716,
...@@ -77,7 +85,8 @@ ...@@ -77,7 +85,8 @@
"category":"gear", "category":"gear",
"price [Euro]":0.54, "price [Euro]":0.54,
"mass [g]":0.6, "mass [g]":0.6,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":12, "delivery time [days]":12,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=4716#T=C" "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=4716#T=C"
} }
} }
\ No newline at end of file
File deleted
File deleted
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
"item number":"8882-1", "item number":"8882-1",
"item description":"Power Functions XL-Motor", "item description":"Power Functions XL-Motor",
"category":"motor", "category":"motor",
"related items":8881, "related items":"8881-1",
"idle current [mA]":80, "idle current [mA]":80,
"idle speed [rev per min]":220, "idle speed [rev per min]":220,
"locking torque [Ncm]":40, "locking torque [Ncm]":40,
"price [Euro]":30, "price [Euro]":30,
"mass [g]":"72,85", "mass [g]":72.85,
"environmental impact [kg CO2e /kg]":15.84,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?S=8882-1&name=Power%20Functions%20XL-Motor&category=%5BPower%20Functions%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?S=8882-1",
"compatible battery":8881,
"input voltage [V]":9, "input voltage [V]":9,
"dimensions [cm]":"13.6 x 12.2 x 3.9" "dimensions [cm]":"13.6 x 12.2 x 3.9"
}, },
...@@ -19,15 +19,15 @@ ...@@ -19,15 +19,15 @@
"item number":"8883-1", "item number":"8883-1",
"item description":"Power Functions M-Motor", "item description":"Power Functions M-Motor",
"category":"motor", "category":"motor",
"related items":"8881, 8878, 45517, 88000", "related items":"8881-1",
"idle current [mA]":65, "idle current [mA]":65,
"idle speed [rev per min]":405, "idle speed [rev per min]":405,
"locking torque [Ncm]":11, "locking torque [Ncm]":11,
"price [Euro]":20, "price [Euro]":20,
"mass [g]":35, "mass [g]":35.0,
"environmental impact [kg CO2e /kg]":15.84,
"delivery time [days]":5, "delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?S=8883-1&name=Power%20Functions%20M-Motor&category=%5BPower%20Functions%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?S=8883-1",
"compatible battery":"8881, 8878, 45517, 88000",
"input voltage [V]":9, "input voltage [V]":9,
"dimensions [cm]":"13.8 x 11.9 x 2.3" "dimensions [cm]":"13.8 x 11.9 x 2.3"
}, },
...@@ -35,15 +35,15 @@ ...@@ -35,15 +35,15 @@
"item number":"88003-1", "item number":"88003-1",
"item description":"Power Functions L-Motor", "item description":"Power Functions L-Motor",
"category":"motor", "category":"motor",
"related items":"8881, 8878, 88000", "related items":"8881-1",
"idle current [mA]":120, "idle current [mA]":120,
"idle speed [rev per min]":390, "idle speed [rev per min]":390,
"locking torque [Ncm]":18, "locking torque [Ncm]":18,
"price [Euro]":15, "price [Euro]":15,
"mass [g]":48, "mass [g]":48.0,
"environmental impact [kg CO2e /kg]":15.84,
"delivery time [days]":3, "delivery time [days]":3,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?S=88003-1&name=Power%20Functions%20L-Motor&category=%5BPower%20Functions%5D#T=S&O={%22iconly%22:0}", "data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?S=88003-1",
"compatible battery":"8881, 8878, 88000",
"input voltage [V]":9, "input voltage [V]":9,
"dimensions [cm]":"13 x 11 x 2.5" "dimensions [cm]":"13 x 11 x 2.5"
} }
......
{
"3482":{
"item number":3482,
"item description":"Wheel with Split Axle Hole",
"category":"rim",
"related items":3483,
"diameter [mm]":17.5,
"price [Euro]":0.01,
"mass [g]":0.88,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3482"
},
"6014":{
"item number":6014,
"item description":"Wheel 11mm D. x 12mm, Hole Round for Wheels Holder Pin",
"category":"rim",
"related items":6015,
"diameter [mm]":11.0,
"price [Euro]":0.0045,
"mass [g]":0.6,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=6014a"
},
"18976":{
"item number":18976,
"item description":"Wheel 18mm D. x 12mm with Axle Hole and Stud, Solid Brake Rotor Lines",
"category":"rim",
"related items":30391,
"diameter [mm]":18.0,
"price [Euro]":0.01,
"mass [g]":1.4,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=18976"
},
"56904":{
"item number":56904,
"item description":"Wheel 30mm D. x 14mm",
"category":"rim",
"related items":30699,
"diameter [mm]":30.0,
"price [Euro]":0.01,
"mass [g]":4.1,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=56904"
},
"56908":{
"item number":56908,
"item description":"Wheel 43.2mm D. x 26mm Technic Racing Small, 6 Pin Holes",
"category":"rim",
"related items":"41897, 61480",
"diameter [mm]":43.2,
"price [Euro]":0.05,
"mass [g]":9.0,
"environmental impact [kg CO2e /kg]":32.06,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=56908"
}
}
\ No newline at end of file
{
"3483":{
"item number":3483,
"item description":"Tire 24mm D. x 8mm Offset Tread - Interior Ridges",
"category":"tire",
"related items":3482,
"inner diameter [mm]":17,
"outer diameter [mm]":24.0,
"price [Euro]":0.0025,
"mass [g]":1.69,
"environmental impact [kg CO2e /kg]":8.97,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=3483"
},
"6015":{
"item number":6015,
"item description":"Tire 21mm D. x 12mm - Offset Tread Small Wide",
"category":"tire",
"related items":6014,
"inner diameter [mm]":11,
"outer diameter [mm]":21.0,
"price [Euro]":0.0004,
"mass [g]":2.0,
"environmental impact [kg CO2e /kg]":8.97,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=6015"
},
"30391":{
"item number":30391,
"item description":"Tire 30.4 x 14 Offset Tread",
"category":"tire",
"related items":18976,
"inner diameter [mm]":17,
"outer diameter [mm]":30.4,
"price [Euro]":0.0027,
"mass [g]":6.5,
"environmental impact [kg CO2e /kg]":8.97,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=30391"
},
"30699":{
"item number":30699,
"item description":"Tire 43.2 x 14 Solid",
"category":"tire",
"related items":56904,
"inner diameter [mm]":30,
"outer diameter [mm]":43.2,
"price [Euro]":0.06,
"mass [g]":8.25,
"environmental impact [kg CO2e /kg]":8.97,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=30699"
},
"41897":{
"item number":41897,
"item description":"Tire 56 x 28 ZR Street",
"category":"tire",
"related items":56908,
"inner diameter [mm]":43,
"outer diameter [mm]":56.0,
"price [Euro]":0.29,
"mass [g]":13.98,
"environmental impact [kg CO2e /kg]":8.97,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=41897"
},
"61480":{
"item number":61480,
"item description":"Tire 68.7 x 34 R",
"category":"tire",
"related items":56908,
"inner diameter [mm]":43,
"outer diameter [mm]":68.7,
"price [Euro]":0.02,
"mass [g]":20.0,
"environmental impact [kg CO2e /kg]":8.97,
"delivery time [days]":5,
"data source":"https://www.bricklink.com/v2/catalog/catalogitem.page?P=61480"
}
}
\ No newline at end of file
{
"4265":{
"item number":4265,
"item description":"wheel 14",
"category":"wheel",
"related items":59895,
"price [Euro]":0.02,
"mass [g]":0.5,
"delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=4265cc01#T=C",
"diameter [mm]":14.0
},
"3482":{
"item number":3482,
"item description":"wheel 24",
"category":"wheel",
"related items":3483,
"price [Euro]":0.01,
"mass [g]":3.0,
"delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=3482c01#T=C",
"diameter [mm]":24.0
},
"56904":{
"item number":56904,
"item description":"wheel 43,2",
"category":"wheel",
"related items":30699,
"price [Euro]":0.11,
"mass [g]":13.0,
"delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=56904c02#T=C",
"diameter [mm]":43.2
},
"41896":{
"item number":41896,
"item description":"wheel 56",
"category":"wheel",
"related items":41897,
"price [Euro]":0.45,
"mass [g]":23.0,
"delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=41896c04#T=C",
"diameter [mm]":56.0
},
"2903":{
"item number":2903,
"item description":"wheel 81,6",
"category":"wheel",
"related items":2902,
"price [Euro]":1.31,
"mass [g]":30.0,
"delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=2903c02#T=C",
"diameter [mm]":81.6
},
"88517":{
"item number":88517,
"item description":"wheel 100,6",
"category":"wheel",
"related items":11957,
"price [Euro]":1.25,
"mass [g]":40.0,
"delivery time [days]":5,
"data source":"https:\/\/www.bricklink.com\/v2\/catalog\/catalogitem.page?P=88517c02#T=C",
"diameter [mm]":100.6
}
}
\ No newline at end of file
figures/leocad_screenshot_cut.png

220 KiB

...@@ -2,42 +2,35 @@ ...@@ -2,42 +2,35 @@
File consists of several functions for the calculation rules of FAIR Quality KPIs File consists of several functions for the calculation rules of FAIR Quality KPIs
""" """
from functions.classes import *
def test_function(): def test_function():
"""Test function to check module functionality""" """Test function to check module functionality"""
print("You called the test function.") print("You called the test function.")
def kpi_mass(system: LegoAssembly)->float:
def kpi_sum(*args):
""" """
Calculates the sum of one or more integers or lists. Calculates the total mass of the system
Args: Args:
*args: One or more integers or lists. system (LegoAssembly): LegoAssembly object that represents the system
Returns: Returns:
total (int): The sum of all given integers and/or the sum total_mass (float): Sum of masses of all components in the system in g
of all items in all given lists.
Raises: Raises:
TypeError: If an argument with unsupported type is passed TypeError: If an argument with unsupported type is passed
(i.e. anything other than int or list). (i.e. anything other than LegoAssembly).
""" """
total = 0 if not isinstance(system, LegoAssembly):
for arg in args: raise TypeError(f"Unsupported type {type(system)} passed to kpi_mass()")
if isinstance(arg, int): # Check if argument is an integer
total += arg
elif isinstance(arg, list): # Check if argument is a list
total += sum(arg)
else:
raise TypeError(
f"Unsupported type {type(arg)} passed to kpi_sum()")
return total
# if arguments are handed over not as a list: sum(list(args))
total_mass = 0
for c in system.get_component_list(-1):
total_mass += c.properties["mass [g]"]
return total_mass # alternative: sum(c.properties["mass [g]"] for c in system.get_component_list(-1))
# Add new functions for calculating some real complicated metrics # Add new functions for calculating metrics
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -2,17 +2,19 @@ ...@@ -2,17 +2,19 @@
File consists of several classes to model elements and assembly File consists of several classes to model elements and assembly
layers of a composed device. layers of a composed device.
""" """
from __future__ import annotations from __future__ import annotations
import uuid import uuid
import json import json
import operator import operator
from enum import Enum, auto from enum import Enum, auto
from typing import List, Dict, Optional from typing import Any, Optional
from copy import deepcopy
class AggregationLayer(Enum): class AggregationLayer(Enum):
"""Describes the levl of aggregation for the objects LegoComponent """Describes the level of aggregation for the objects LegoComponent and LegoAssembly
and LegoAssembly and provides the 4 applicable layers. and provides the 4 applicable layers.
""" """
SYSTEM = auto() SYSTEM = auto()
...@@ -22,151 +24,195 @@ class AggregationLayer(Enum): ...@@ -22,151 +24,195 @@ class AggregationLayer(Enum):
class LegoComponent: class LegoComponent:
""" """Information storage for a single Lego component.
A class for storing information about a single Lego component.
...
Attributes Attributes
---------- uuid (uuid.UUID): A randomly generated unique identifier for the component.
uuid : UUID parent (None | LegoAssembly): The parent of the component. None if the
A randomly generated unique identifier for the component. component has no parent.
parent : None | LegoAssembly layer (AggregationLayer): An enumeration indicating the hierarchy level. For
The parent of the component. Can be either None or a LegoAssembly object. components, this is COMPONENT by default.
layer : AggregationLayer properties (dict[str, Any]): dictionary that holds all properties of the
An enumeration indicating the hierarchy level. For compoennts, this is component that can be saved in a dictionary format.
COMPONENT by default.
properties : dict
Dictionary that holds all properties of the component that can be saved
in a dictionary format.
Methods Methods
------- clone(new_label=None): Returns a new instance of LegoComponent identical to the
clone(new_label=None) current instance.
Returns a new instance of LegoComponent identical to the current instance. get_root_assembly(): Returns the top-level assembly in which the component is
get_root_assembly() contained.
Returns the top-level assembly in which the component belongs. to_dict(): Returns the current instance represented as a dictionary.
to_dict()
Returns the current instance represented as a dictionary.
""" """
def __init__( def __init__(
self, self,
label: Optional[str] = None, label: Optional[str] = None,
datasheet: Optional[dict] = None, datasheet: Optional[dict[str, Any]] = None,
*more_properties: dict, *more_properties: dict,
**kwargs, **kwargs,
) -> None: ) -> None:
""" """Create a LegoComponent object.
Constructs all the necessary attributes for the LegoComponent object.
Parameters Args:
---------- label (str, optional): The name of the component to add. Defaults
label : str, optional to None.
The name of the component to add. datasheet (dict[str, Any], optional): Metadata describing the component,
datasheet : dict, optional read from datasheet. Defaults to None.
Metadata describing the component, read from datasheet. more_properties (dict[str, Any])
more_properties : tuple of dict, dict
Additional dictionaries representing custom properties. Additional dictionaries representing custom properties.
kwargs kwargs
Arbitrary keyword arguments representing custom properties. Arbitrary keyword arguments representing custom properties.
Raises Raises:
------ ValueError: If the type of more_properties argument is not a dictionary.
ValueError
If the type of more_properties argument is not a dictionary.
""" """
self.uuid: uuid.UUID = uuid.uuid4()
self.parent: None | LegoAssembly = None self._uuid: uuid.UUID = uuid.uuid4()
self._parent: None | LegoAssembly = None
self.layer: AggregationLayer = AggregationLayer.COMPONENT self.layer: AggregationLayer = AggregationLayer.COMPONENT
self.properties: dict = {} self.properties: dict = {}
if label is not None: if label is not None:
self.properties["label"] = label self.properties["label"] = label
if datasheet is not None: if datasheet is not None:
self.properties.update(datasheet) self.properties.update(deepcopy(datasheet))
for prop in more_properties: for prop in more_properties:
if isinstance(prop, dict): if isinstance(prop, dict):
self.properties.update(prop) self.properties.update(deepcopy(prop))
else: else:
raise ValueError(f"Unexpected argument type: {type(more_properties)}") raise ValueError(f"Unexpected argument type: {type(more_properties)}")
for key, value in kwargs.items(): for key, value in kwargs.items():
self.properties[key] = value self.properties[key] = deepcopy(value)
@property
def uuid(self) -> uuid.UUID:
return self._uuid
@property
def parent(self) -> None | LegoAssembly:
return self._parent
def clone(self, new_label: Optional[str] = None) -> LegoComponent: def clone(self, new_label: Optional[str] = None) -> LegoComponent:
""" """Return a new instance of LegoComponent identical to the current instance.
Returns a new instance of LegoComponent identical to the current instance.
This method creates a new instance of LegoComponent that is identical to the This method creates a new instance of LegoComponent that is identical to the
current instance with an optional different label. current instance with an optional different label.
The assigned uuid changes and elements are copied. The assigned uuid changes and elements are copied.
Parameters Args:
---------- new_label (str, optional): A new label string to use. Defaults to None.
new_label : str, optional
A new label string to use. Defaults to None.
Returns
-------
LegoComponent
A new instance of LegoComponent that is identical to the current instance.
Returns:
LegoComponent: A new instance of LegoComponent that is identical to the
current instance.
""" """
if new_label is None: if new_label is None:
new_label = self.properties["label"] new_label = self.properties["label"]
clone = LegoComponent(None, None, self.properties) clone = LegoComponent(None, None, deepcopy(self.properties))
clone.properties["label"] = new_label clone.properties["label"] = new_label
return clone return clone
def get_root_assembly(self): def get_property(self, key: str, default: Any = None) -> Any:
"""Get a property from the component.
Args:
key (str): Property name.
default (Any, optional): What to return if key is not available in
properties. Defaults to None.
Returns:
Any: Property value.
""" """
Returns the top-level assembly in which the component belongs. if key == "uuid":
return self._uuid
if key == "layer":
return self._layer
return self.properties.get(key, default=default)
def get_root_assembly(self):
"""Return the top-level assembly in which the component belongs.
This method traverses the parent hierarchy of a LegoComponent object until it This method traverses the parent hierarchy of a LegoComponent object until it
finds the root-level LegoAssembly, returning it to the caller. A parent is finds the root-level LegoAssembly, returning it to the caller. A parent is
assigned when a LegoComponent or LegoItem is added to a LegoAssembly object. assigned when a LegoComponent or LegoItem is added to a LegoAssembly object.
Returns Returns:
------- None | LegoAssembly: The root-level LegoAssembly or None if the component
None | LegoAssembly has no parent.
The root-level LegoAssembly or None if the component has no parent.
""" """
if self.parent is None: if self._parent is None:
return None return None
current_assembly = self.parent current_assembly = self._parent
while current_assembly.parent is not None: while current_assembly._parent is not None:
current_assembly = current_assembly.parent current_assembly = current_assembly._parent
return current_assembly return current_assembly
def to_dict(self) -> Dict: def to_dict(self) -> dict:
""" """Return the current instance represented as a dictionary.
Returns the current instance represented as a dictionary.
This method returns a dictionary representation of the LegoComponent object This method returns a dictionary representation of the LegoComponent object
suitable for serialization as JSON. suitable for serialization as JSON.
Returns Returns:
------- dict[str, Any]: A dictionary representation of the object.
dict
A dictionary representation of the object.
""" """
dict_ = { dict_ = {
"uuid": self.uuid, "uuid": self._uuid,
"properties": self.properties, "properties": self.properties,
"layer": self.layer, "layer": self.layer,
} }
return {"component": dict_} return {"component": dict_}
def __str__(self): def __eq__(self, obj: object):
""" """Check if provided object is equal to this component.
Simple string representation of the object.
Args:
obj (object): Object to compare to.
Returns:
bool: True if UUID, properties, and layer match. False otherwise.
""" """
return self.__repr__() # in case of mismatching class
if not isinstance(obj, LegoComponent):
return False
if (
self._uuid == obj._uuid
and self.layer == obj.layer
and self.properties == obj.properties
):
return True
else:
return False
def __repr__(self): def __repr__(self):
"""Create a machine-readable representation of the instance.
Returns:
str: A string representing the LegoComponent instance.
"""
label_str = ""
if self.properties["label"] is not None:
label_str = f"label='{self.properties['label']}', "
property_str = ", ".join(
[
f"'{k}': '{v}'" if isinstance(v, str) else f"'{k}': {v}"
for k, v in self.properties.items()
if not k == "label"
]
)
property_str = f"**{{{property_str}}}" if property_str else property_str
return f"LegoComponent({label_str}{property_str})"
def __str__(self):
"""Handle the conversion of LegoComponent objects to str objects.
Returns:
str: A string converted from the LegoComponent instance.
""" """
String representation of the object including the component label and UUID. if self.properties.get("label") is None:
""" return f"LegoComponent [{self._uuid}]"
return f"LegoComponent {self.properties['label']} [{self.uuid}]" return f"LegoComponent {self.properties['label']} [{self._uuid}]"
class LegoAssembly: class LegoAssembly:
...@@ -178,8 +224,8 @@ class LegoAssembly: ...@@ -178,8 +224,8 @@ class LegoAssembly:
parent (LegoAssembly or None): The parent assembly containing this one, if any. parent (LegoAssembly or None): The parent assembly containing this one, if any.
properties (dict): Optional properties for the assembly, such as a label. properties (dict): Optional properties for the assembly, such as a label.
layer (AggregationLayer): The aggregation layer of the assembly. layer (AggregationLayer): The aggregation layer of the assembly.
components (List[LegoComponent]): The list of contained components. components (list[LegoComponent]): The list of contained components.
assemblies (List[LegoAssembly]): The list of contained subassemblies. assemblies (list[LegoAssembly]): The list of contained subassemblies.
""" """
def __init__( def __init__(
...@@ -199,29 +245,49 @@ class LegoAssembly: ...@@ -199,29 +245,49 @@ class LegoAssembly:
properties (dict): Optional properties for the assembly, such as a label. properties (dict): Optional properties for the assembly, such as a label.
**kwargs: Optional keyword arguments for additional properties. **kwargs: Optional keyword arguments for additional properties.
""" """
self.uuid: uuid.UUID = uuid.uuid4() self._uuid: uuid.UUID = uuid.uuid4()
self.parent: None | LegoAssembly = None self._parent: None | LegoAssembly = None
self.properties: dict = {} self.properties: dict = {}
self.layer: AggregationLayer = layer self._layer: AggregationLayer = layer
if label is not None: if label is not None:
self.properties["label"] = label self.properties["label"] = label
for prop in properties: for prop in properties:
if isinstance(prop, dict): if isinstance(prop, dict):
self.properties.update(prop) self.properties.update(deepcopy(prop))
else: else:
raise ValueError(f"Unexpected argument type: {type(properties)}") raise ValueError(f"Unexpected argument type: {type(properties)}")
for key, value in kwargs.items(): for key, value in kwargs.items():
self.properties[key] = value self.properties[key] = deepcopy(value)
self.components: List[LegoComponent] = [] self._components: list[LegoComponent] = []
self.assemblies: List[LegoAssembly] = [] self._assemblies: list[LegoAssembly] = []
@property
def uuid(self) -> uuid.UUID:
return self._uuid
@property
def parent(self) -> None | LegoAssembly:
return self._parent
@property
def layer(self) -> AggregationLayer:
return self._layer
@property
def components(self) -> list[LegoComponent]:
return self._components
@property
def assemblies(self) -> list[LegoAssembly]:
return self._assemblies
def add_component(self, component: LegoComponent | List[LegoComponent]) -> None: def add_component(self, component: LegoComponent | list[LegoComponent]) -> None:
""" """
Adds a Lego component to the current assembly. Adds a Lego component to the current assembly.
Use add() to handle both components and assemblies. Use add() to handle both components and assemblies.
Args: Args:
component (LegoComponent or List[LegoComponent]): component (LegoComponent or list[LegoComponent]):
The component or list of components to add. The component or list of components to add.
Raises: Raises:
...@@ -241,22 +307,22 @@ class LegoAssembly: ...@@ -241,22 +307,22 @@ class LegoAssembly:
f"got {type(component).__name__} instead." f"got {type(component).__name__} instead."
) )
if self.get_root_assembly().contains_uuid(component.uuid): if self.get_root_assembly().contains_uuid(component._uuid):
raise AssertionError( raise AssertionError(
f"This assembly or a subassembly already contains " f"This assembly or a subassembly already contains "
f"the component with ID " f"the component with ID "
f"{component.uuid}." f"{component._uuid}."
) )
component.parent = self component._parent = self
self.components.append(component) self._components.append(component)
def add_assembly(self, assembly: LegoAssembly | List[LegoAssembly]) -> None: def add_assembly(self, assembly: LegoAssembly | list[LegoAssembly]) -> None:
""" """
Adds a subassembly to the current assembly. Adds a subassembly to the current assembly.
Use add() to handle both components and assemblies. Use add() to handle both components and assemblies.
Args: Args:
assembly (LegoAssembly or List[LegoAssembly]): assembly (LegoAssembly or list[LegoAssembly]):
The subassembly or list of subassemblies to add. The subassembly or list of subassemblies to add.
Raises: Raises:
...@@ -276,23 +342,23 @@ class LegoAssembly: ...@@ -276,23 +342,23 @@ class LegoAssembly:
f"got {type(assembly).__name__} instead." f"got {type(assembly).__name__} instead."
) )
if self.get_root_assembly().contains_uuid(assembly.uuid): if self.get_root_assembly().contains_uuid(assembly._uuid):
raise AssertionError( raise AssertionError(
f"This assembly or a subassembly already contains " f"This assembly or a subassembly already contains "
f"the assembly with ID {assembly.uuid}." f"the assembly with ID {assembly._uuid}."
) )
assembly.parent = self assembly._parent = self
self.assemblies.append(assembly) self._assemblies.append(assembly)
def add( def add(
self, part: LegoAssembly | LegoComponent | List[LegoAssembly | LegoComponent] self, part: LegoAssembly | LegoComponent | list[LegoAssembly | LegoComponent]
) -> None: ) -> None:
""" """
Adds either a Lego component, a subassembly or a (mixed) list of them to the Adds either a Lego component, a subassembly or a (mixed) list of them to the
current assembly. Uses internal functions add_component() and add_assembly(). current assembly. Uses internal functions add_component() and add_assembly().
Args: Args:
part (LegoAssembly or LegoComponent or List[LegoAssembly or LegoComponent]): part (LegoAssembly or LegoComponent or list[LegoAssembly or LegoComponent]):
The part or parts to add. The part or parts to add.
Raises: Raises:
...@@ -313,7 +379,7 @@ class LegoAssembly: ...@@ -313,7 +379,7 @@ class LegoAssembly:
f"Got {type(part).__name__} instead." f"Got {type(part).__name__} instead."
) )
def children(self) -> Dict[str, List[LegoComponent] | List[LegoAssembly]]: def children(self) -> dict[str, list[LegoComponent] | list[LegoAssembly]]:
""" """
Returns a dictionary of the assembly's children (components and Returns a dictionary of the assembly's children (components and
sub-assemblies), sorted by category. sub-assemblies), sorted by category.
...@@ -322,28 +388,45 @@ class LegoAssembly: ...@@ -322,28 +388,45 @@ class LegoAssembly:
A dictionary with two keys - "components" and "assemblies" - and A dictionary with two keys - "components" and "assemblies" - and
corresponding lists of LegoComponents and LegoAssemblies as values. corresponding lists of LegoComponents and LegoAssemblies as values.
""" """
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]: def get_component_list(self, max_depth: int = -1) -> list[LegoComponent]:
""" """
Gets a full list of all components contained in the assembly or any of Gets a full list of all components contained in the assembly or any of
its sub-assemblies. its sub-assemblies.
Args: Args:
max_depth: An integer indicating the maximum depth of recursion. max_depth: An integer indicating the maximum depth of recursion.
-1 means unlimited depth. 0 returns only direc children. -1 means unlimited depth. 0 returns only direct children.
Returns: Returns:
A list of all LegoComponents contained in the current assembly or A list of all LegoComponents contained in the current assembly or
its sub-assemblies. its sub-assemblies.
""" """
component_list = [] component_list = []
component_list.extend(self.components) component_list.extend(self._components)
if max_depth > 0 or max_depth < 0: if not max_depth == 0:
for assembly in self.assemblies: for assembly in self._assemblies:
component_list.extend(assembly.get_component_list(max_depth - 1)) component_list.extend(assembly.get_component_list(max_depth - 1))
return component_list return component_list
def get_property(self, key: str, default: Any = None) -> Any:
"""Get a property from the assembly.
Args:
key (str): Property name.
default (Any, optional): What to return if key is not available in
properties. Defaults to None.
Returns:
Any: Property value.
"""
if key == "uuid":
return self._uuid
if key == "layer":
return self._layer
return self.properties.get(key, default=default)
def get_root_assembly(self) -> LegoAssembly: def get_root_assembly(self) -> LegoAssembly:
""" """
Returns the root LegoAssembly of the current assembly by recursively Returns the root LegoAssembly of the current assembly by recursively
...@@ -353,8 +436,8 @@ class LegoAssembly: ...@@ -353,8 +436,8 @@ class LegoAssembly:
The root LegoAssembly of the current LegoAssembly instance. The root LegoAssembly of the current LegoAssembly instance.
""" """
current_assembly = self current_assembly = self
while current_assembly.parent is not None: while current_assembly._parent is not None:
current_assembly = current_assembly.parent current_assembly = current_assembly._parent
return current_assembly return current_assembly
def contains_uuid(self, uuid_: uuid.UUID): def contains_uuid(self, uuid_: uuid.UUID):
...@@ -370,18 +453,18 @@ class LegoAssembly: ...@@ -370,18 +453,18 @@ class LegoAssembly:
True if the UUID exists in the assembly or any of its sub-assemblies, True if the UUID exists in the assembly or any of its sub-assemblies,
False otherwise. False otherwise.
""" """
component_ids = list(map(lambda c: c.uuid, self.components)) component_ids = list(map(lambda c: c._uuid, self._components))
if uuid_ in component_ids: if uuid_ in component_ids:
return True return True
assembly_ids = list(map(lambda a: a.uuid, self.assemblies)) assembly_ids = list(map(lambda a: a._uuid, self._assemblies))
if uuid_ in assembly_ids: if uuid_ in assembly_ids:
return True return True
for assembly in self.assemblies: for assembly in self._assemblies:
if assembly.contains_uuid(uuid_): if assembly.contains_uuid(uuid_):
return True return True
return False return False
def to_dict(self) -> Dict: def to_dict(self) -> dict:
""" """
Serializes the current LegoAssembly instance and its descendants into a dict. Serializes the current LegoAssembly instance and its descendants into a dict.
...@@ -390,20 +473,14 @@ class LegoAssembly: ...@@ -390,20 +473,14 @@ class LegoAssembly:
of its component and sub-assembly children. of its component and sub-assembly children.
""" """
dict_ = { dict_ = {
"uuid": self.uuid, "uuid": self._uuid,
"properties": self.properties, "properties": self.properties,
"layer": self.layer, "layer": self._layer,
} }
dict_["components"] = [component.to_dict() for component in self.components] dict_["components"] = [component.to_dict() for component in self._components]
dict_["assemblies"] = [assembly.to_dict() for assembly in self.assemblies] dict_["assemblies"] = [assembly.to_dict() for assembly in self._assemblies]
return {"assembly": dict_} return {"assembly": dict_}
def __repr__(self):
"""
String representation of the object including the component label and UUID.
"""
return f"LegoAssembly {self.properties['label']} [{self.uuid}]"
def clone(self, label: Optional[str] = None) -> LegoAssembly: def clone(self, label: Optional[str] = None) -> LegoAssembly:
""" """
Creates a deep clone of the current LegoAssembly instance, including Creates a deep clone of the current LegoAssembly instance, including
...@@ -419,49 +496,102 @@ class LegoAssembly: ...@@ -419,49 +496,102 @@ class LegoAssembly:
""" """
if label is None: if label is None:
label = self.properties["label"] label = self.properties["label"]
clone = LegoAssembly(self.layer, None, self.properties) clone = LegoAssembly(self._layer, None, deepcopy(self.properties))
clone.properties["label"] = label clone.properties["label"] = label
for component in self.components: for component in self._components:
clone.add_component(component.clone()) clone.add_component(component.clone())
for assembly in self.assemblies: for assembly in self._assemblies:
clone.add_assembly(assembly.clone()) clone.add_assembly(assembly.clone())
return clone return clone
def __eq__(self, obj: object) -> bool:
"""Check if provided object is equal to this assembly.
Args:
obj (object): Object to compare to.
Returns:
bool: True if UUID, properties, layer, components and assemblies match.
False otherwise.
"""
# in case of mismatching class
if not isinstance(obj, LegoAssembly):
return False
if (
self._uuid == obj._uuid
and self.properties == obj.properties
and self._layer == obj._layer
and self._components == obj._components
and self._assemblies == obj._assemblies
):
return True
else:
return False
def __repr__(self):
"""Create a machine-readable representation of the instance.
def print_assembly_tree(root, level=0, is_last=False): Returns:
str: A string representing the LegoAssembly instance.
"""
label_str = ""
if self.properties["label"] is not None:
label_str = f"label='{self.properties['label']}', "
layer_str = f"layer={self._layer}, "
property_str = ", ".join(
[
f"'{k}': '{v}'" if isinstance(v, str) else f"'{k}': {v}"
for k, v in self.properties.items()
if not k == "label"
]
)
property_str = f"**{{{property_str}}}" if property_str else property_str
return f"LegoAssembly({label_str}{layer_str}{property_str})"
def __str__(self):
"""Handle the conversion of LegoComponent objects to str objects.
Returns:
str: A string converted from the LegoComponent instance.
"""
if self.properties["label"] is None:
return f"LegoAssembly [{self._uuid}]"
return f"LegoAssembly {self.properties['label']} [{self._uuid}]"
def print_assembly_tree(root: LegoAssembly, levels: list[bool] = None) -> None:
""" """
Prints the assembly tree starting from root with a visualization Prints the assembly tree starting from root with a visualization
implemented with text characters. implemented with text characters.
Args: Args:
root (LegoAssembly): The root of the assembly tree to print. root (LegoAssembly): The root of the assembly tree to print.
level (int): The indentation level. Defaults to 0. levels (list[bool]): Internally used by recursion to know where
is_last (bool): Determines whether the current node is the last in level. to print vertical connection. Defaults to None.
Defaults to False.
""" """
if not isinstance(root, LegoAssembly): if not isinstance(root, LegoAssembly):
raise TypeError( raise TypeError(
f"Argument should be of type {LegoAssembly.__name__}, " f"Argument should be of type {LegoAssembly.__name__}, "
f"got {type(root).__name__} instead." f"got {type(root).__name__} instead."
) )
""" Print the items. """ if levels is None:
levels = []
connection_padding = "".join(map(lambda draw: "" if draw else " ", levels))
assembly_padding = "" assembly_padding = ""
if level > 0: if len(levels) > 0:
assembly_padding += "" * (level - 1) assembly_padding = "├── " if levels[-1] else "└── "
if is_last: print(f"{connection_padding[:-4]}{assembly_padding}{root}")
assembly_padding += "└── "
else:
assembly_padding += "├── "
print(f"{assembly_padding}{root}")
""" Recursively print child components. """ """ Recursively print child components. """
for i, assembly in enumerate(root.assemblies): for i, assembly in enumerate(root._assemblies):
is_last_ = i == len(root.assemblies) - 1 and len(root.components) == 0 is_last = i == len(root._assemblies) - 1 and len(root._components) == 0
print_assembly_tree(assembly, level + 1, is_last_) print_assembly_tree(assembly, [*levels, not is_last])
""" Print the components. """ """ Print the components. """
for i, item in enumerate(root.components): for i, component in enumerate(root._components):
component_padding = "" * level if not is_last else " " component_padding = "├── " if i < len(root._components) - 1 else "└── "
component_padding += "├── " if i < len(root.components) - 1 else "└── " print(f"{connection_padding}{component_padding}{component}")
print(f"{component_padding}{item}")
def correct_aggregation_hierarchy(root: LegoAssembly, strict: bool = False): def correct_aggregation_hierarchy(root: LegoAssembly, strict: bool = False):
...@@ -485,11 +615,11 @@ def correct_aggregation_hierarchy(root: LegoAssembly, strict: bool = False): ...@@ -485,11 +615,11 @@ def correct_aggregation_hierarchy(root: LegoAssembly, strict: bool = False):
higher_level = operator.le higher_level = operator.le
if strict: if strict:
higher_level = operator.lt higher_level = operator.lt
for component in root.components: for component in root._components:
if not higher_level(root.layer.value, component.layer.value): if not higher_level(root._layer.value, component.layer.value):
return False return False
for assembly in root.assemblies: for assembly in root._assemblies:
if not higher_level(root.layer.value, assembly.layer.value): if not higher_level(root._layer.value, assembly._layer.value):
return False return False
if not correct_aggregation_hierarchy(assembly, strict): if not correct_aggregation_hierarchy(assembly, strict):
return False return False
...@@ -501,12 +631,12 @@ class KPIEncoder(json.JSONEncoder): ...@@ -501,12 +631,12 @@ class KPIEncoder(json.JSONEncoder):
JSON encoder that handles special class types for KPI serialization. JSON encoder that handles special class types for KPI serialization.
""" """
def default(self, o): def default(self, o: Any):
""" """
Overrides default method to handle special conversion cases. Overrides default method to handle special conversion cases.
Args: Args:
o : Object to be converted. o (Any): Object to be converted.
Returns: Returns:
Converted object or super method if no applicable case is found. Converted object or super method if no applicable case is found.
......