Files
CreoMigrate/lib/manage_configs.py
T

423 lines
16 KiB
Python

import os
import json
from configparser import ConfigParser
import pandas as pd
"""
Diese Lib verwaltet alle Config dateien (als .cfg oder als .json) von CreoMigrate
"""
class JsonBasedConfig:
"""Klasse mit generellen Methoden für alle Config Objekte mit json files"""
def __init__(self, config_dir, config_file):
"""merke dir den Pfad zu den Configdateien"""
self.config_path = os.path.join(config_dir, config_file)
def load_json_from_file(self):
"""lade das json file vom gegeben Pfad in ein json Objekt"""
with open(self.config_path, 'r', encoding='utf-8') as fh:
return json.loads(fh.read())
def from_json(self, json_str):
"""nimm den Json String zur Initialisierung des Objektes"""
return json.loads(json_str)
class CSVBasedConfig:
def __init__(self, config_dir, config_file):
"""merke dir den Pfad zu den Configdateien"""
self.config_path = os.path.join(config_dir, config_file)
def load_csv_from_file(self):
"""lade die csv aus dem angegebenen Dateipfad in ein Dataframe"""
return pd.read_csv(self.config_path, sep=";", dtype="object")
class SivasAssemblyData(JsonBasedConfig):
"""Auslesen der Assembly-Informationen zu einer angegebenen Baugruppennummer"""
def __init__(self, config_dir, config_file):
super().__init__(config_dir, config_file)
self.assembly_data = None
def _lazy_init_assembly_data(func):
"""Sorgt dafür, dass json-Datei der Baugruppe nur 1x geladen wird"""
def wrapper(self, *args, **kwargs):
if self.assembly_data is None:
self.assembly_data = self.load_json_from_file()
return func(self, *args, **kwargs)
return wrapper
@_lazy_init_assembly_data
def get_assembly_ids(self):
"""Gibt alle Teilenummern der Stückliste zurück (auch Teilenummern aus tieferen Baugruppenebenen)"""
assembly_ids = set()
for child_id, parent_properties in self.assembly_data.items():
assembly_ids.add(child_id)
for parent_property in parent_properties:
parent_id = parent_property["Parent"]
assembly_ids.add(parent_id)
return assembly_ids
@_lazy_init_assembly_data
def get_parent_children(self) -> dict:
"""Ordnet Stücklistenpositionen (Kinder) den Baugruppennummern (Eltern) zu. Die Kinder sind entsprechend ihrer Positionsnummern in den Stücklisten sortiert."""
unordered_parent_children = {}
for child_id, parent_properties in self.assembly_data.items():
for parent_property in parent_properties:
parent_id = parent_property["Parent"]
child_posnr_in_parent_bom = int(parent_property["Position"].lstrip(".").split("/")[0])
if parent_id not in unordered_parent_children:
unordered_parent_children[parent_id] = {}
unordered_parent_children[parent_id][child_posnr_in_parent_bom] = child_id
ordered_parent_children = {}
for parent, children in unordered_parent_children.items():
for pos_nr, child in sorted(children.items()):
if parent not in ordered_parent_children:
ordered_parent_children[parent] = []
ordered_parent_children[parent].append(child)
return ordered_parent_children
def get_longest_assembly_id(self) -> str:
"""Gibt die längste Teilenummer von allen Stücklistenpositionen zurück"""
return max(self.get_assembly_ids())
@_lazy_init_assembly_data
def get_max_bom_depth(self) -> int:
"""Gibt die tiefste Stücklistenebene zurück (0 = höchste Ebene)"""
bom_levels = []
for parents in self.assembly_data.values():
for parent in parents:
current_bom_level = parent["Position"].count(".")
bom_levels.append(current_bom_level)
return max(bom_levels)
@_lazy_init_assembly_data
def get_property_names(self) -> list:
"""Gibt alle Eigenschaftsnamen zurück, die in der Assembly-json-Datei enthalten sind"""
for parent_data in self.assembly_data.values():
return list(parent_data[0])
@_lazy_init_assembly_data
def get_property(self, property) -> dict:
"""Gibt eine Eigenschaft zu den Teilenummern der Stückliste zurück (z.B. Bezeichnung)"""
if property not in self.get_property_names():
raise ValueError("Angegebener Eigenschaftsname exisitert nicht!")
id_property_mapping = {}
for child, parent_data in self.assembly_data.items():
for properties in parent_data:
id_property_mapping[child] = properties[property]
return id_property_mapping
class RuleDesignerDatabaseConfig(CSVBasedConfig):
"""Auslesen der Angaben aus der RuleDesigner-Datenbank-CSV"""
def __init__(self, config_dir=".", config_file="RD_Teilenummern.csv"):
super().__init__(config_dir, config_file)
def get_ruledesigner_ids(self):
return tuple(self.load_csv_from_file()['Teilenummern'])
class MigrationStatus:
def __init__(self, file_dir, assembly_json):
self.sivas_assembly_data = SivasAssemblyData(file_dir, assembly_json)
self.ruledesigner_data = RuleDesignerDatabaseConfig(file_dir)
def get_migration_status(self):
"""Gibt zurück, ob die Teilenummern der Baugruppe bereits migriert wurden, oder nicht"""
sivas_assembly_ids = self.sivas_assembly_data.get_assembly_ids()
ruledesigner_ids = self.ruledesigner_data.get_ruledesigner_ids()
migration_status = {}
for sivas_assembly_id in sivas_assembly_ids:
migration_status[sivas_assembly_id] = True if sivas_assembly_id in ruledesigner_ids else False
return migration_status
class TOSGroupsConfig(JsonBasedConfig):
"""Auslesen der Config Angaben zu den TOS Gruppen"""
def __init__(self, config_dir='.', config_file='TOS-Gruppen.json'):
self.tos_hierarchy = None
super().__init__(config_dir, config_file)
def get_children_keys(self, tos_hierarchy, currentId, result):
# wenn eine Referenz nicht weiter in der TOS Hierarchy vorkommt, dann nicht mit einsammeln
if currentId not in tos_hierarchy:
return None
childIds = tos_hierarchy[currentId]["reference-ids"].values()
level = tos_hierarchy[currentId]["TOS-level"]
if len(childIds) > 0:
for cId in childIds:
# prüfen, ob die Referenz sonst noch verwendet wird
if cId in tos_hierarchy:
result[cId] = level
for cId in childIds:
self.get_children_keys(tos_hierarchy, cId, result)
def get_all_keywords(self):
"""hole alle TOS Keywords, welche in der config Datei definiert sind"""
# lazy. nur einmal laden, wenn noch nicht geschehen
if not self.tos_hierarchy:
self.tos_hierarchy = super().load_json_from_file()
# beginne mit root als ersten, vordefiniertem Keyword
rootid = self.tos_hierarchy["root"]
# und von dort aus folge dann allen Referenzen
result = dict()
# root als Nullte Ebene dazu
result[rootid] = 0
self.get_children_keys(self.tos_hierarchy, rootid, result)
return result
def get_keywords_of_level(self, allKeywords, levelnum):
"""hole alle TOS Keywords der gegebenen Hierarchiestufe "levelnum" """
ret = list()
for kw in allKeywords:
if allKeywords[kw] == levelnum:
ret.append(kw)
return ret
def from_json(self, json_str):
"""lade den Json String zur Initialisierung des Objektes"""
self.tos_hierarchy = super().from_json(json_str)
class MaterialTransConfig(JsonBasedConfig):
"""Auslesen der Config zu allen Materialangaben"""
def __init__(self, config_dir='.', config_file='Materialübersetzung.json'):
self.materials = None
super().__init__(config_dir, config_file)
def from_json(self, json_str):
"""lade den Json String zur Initialisierung des Objektes"""
self.materials = super().from_json(json_str)
def get_all_materials(self):
"""hole alle Materialien, welche in der config Datei definiert sind"""
if not self.materials:
self.materials = super().load_json_from_file()
ret = list()
for k in self.materials.keys():
ret.append(k)
return ret
def get_material_of_type(self, mat):
"""hole alle Materialien, welche in der config Datei definiert sind"""
if not self.materials:
self.load_materials()
return self.materials[mat]
class IniBasedConfig:
"""Klasse mit generellen Methoden für alle Config Objekte mit .cfg/.ini files"""
def __init__(self, config_dir, config_file):
"""merke dir den Pfad zu den Configdateien"""
self.config_path = os.path.join(config_dir, config_file)
if not os.path.exists(self.config_path):
raise Exception("path does not exist")
self.isInitalized = False
self.config = ConfigParser(allow_no_value=True)
self.config.optionxform = lambda option: option # preserve case for letters
def from_cfg_str(self, cfg_str):
"""nimm den config String zur Initialisierung des Objektes"""
self.config.read_string(cfg_str)
self.isInitalized = True
def load_cfg_from_file(self):
"""lade das config file vom gegeben Pfad"""
self.config.read(self.config_path, encoding='utf-8')
self.isInitalized = True
def get_section_options(self, section_name):
if not self.isInitalized:
self.load_cfg_from_file()
return self.config.options(section_name)
def sections(self):
if not self.isInitalized:
self.load_cfg_from_file()
return self.config.sections()
def get_int(self, section_name, option):
if not self.isInitalized:
self.load_cfg_from_file()
return self.config.getint(section_name, option)
def get_dict(self, section_name):
if not self.isInitalized:
self.load_cfg_from_file()
return dict(self.config.items(section_name))
class AllgemeinConfig(IniBasedConfig):
"""Auslesen der Config für alle allgemeinen Angaben"""
def __init__(self, config_dir='.', config_file='Allgemein.cfg'):
super().__init__(config_dir, config_file)
def get_sections(self):
""" gibt alle Sektionen zurück wie
Kategorien, Dispogruppe, Dispolaufgruppe, EinheitAusw, ...
"""
return super().sections()
def get_options(self, section_name):
""" get all options below"""
return super().get_section_options(section_name)
class BezeichnerConfig(IniBasedConfig):
"""Auslesen der Config zu allen möglichen Bezeichner"""
def __init__(self, config_dir='.', config_file='Bezeichner.cfg'):
super().__init__(config_dir, config_file)
def get(self):
"""hole alle möglichen Bezeichner """
return super().get_section_options("Bezeichner")
class FarbenConfig(IniBasedConfig):
"""Auslesen der Config zu allen Farbensarten"""
def __init__(self, config_dir='.', config_file='Farben.cfg'):
super().__init__(config_dir, config_file)
def get_farbauswahl(self):
"""hole alle Farben die möglich sind
-> [RAL 1004 Goldgelb, RAL 2000 Gelborange, RAL 3002 Kaminrot, RAL 5010 Enzianblau]
"""
return super().get_section_options("Farbauswahl")
def get_farben_eloxieren(self):
"""hole alle Farben fürs eloxieren
-> [Rot, Grün, Blau, Gelb, Schwarz, Violett, Natur]
"""
return super().get_section_options("FarbeEloxieren")
class FertigungsartConfig(IniBasedConfig):
"""Auslesen der Config zu allen Fertigungsarten"""
def __init__(self, config_dir='.', config_file='Fertigungsarten.cfg'):
super().__init__(config_dir, config_file)
def get_fertigungsarten(self):
"""hole alle Fertigungsarten, welche als Sektionen in der config gegeben sind, also
Fertigungsart, Allgemeintoleranz3DDruck, AllgemeintoleranzGiessen,
AllgemeintoleranzExtrudieren, ...
"""
return super().sections()
def get_normen_of_type(self, ftype):
"""hole alle möglichen Oberflächen Behandlungsarten zum gegebenen Material
Allgemeintoleranz3DDruck ftype
-> [ISO 2768:1991-fH, ISO 2768:1991-fK, ISO 2768:1991-fL, ISO 2768:1991-mH, , ...]
"""
return super().get_section_options(ftype)
class HerstellerConfig(IniBasedConfig):
"""Auslesen der Config zu allen möglichen Hersteller"""
def __init__(self, config_dir='.', config_file='Hersteller.cfg'):
super().__init__(config_dir, config_file)
def get(self):
"""hole alle möglichen Bezeichner """
return super().get_section_options("Hersteller")
class NormenConfig(IniBasedConfig):
"""Auslesen der Config zu allen Normen"""
def __init__(self, config_dir='.', config_file='Normen.cfg'):
super().__init__(config_dir, config_file)
def get_normen(self):
"""hole alle Normen, welche als Sektionen in der config gegeben sind, also
Oberflaechennorm, Kantennorm, Massnorm, Tolerierungsnorm,..
"""
return super().sections()
def get_normen_of_type(self, otype):
"""hole alle möglichen Oberflächen Behandlungsarten zum gegebenen Material
Oberfläche otype
Oberflaechennorm -> [ISO 1302:2002, ISO 21920]
Tolerierungsnorm, ...
"""
return super().get_section_options(otype)
class OberflächeConfig(IniBasedConfig):
"""Auslesen der Config zu allen Oberflächen"""
def __init__(self, config_dir='.', config_file='Oberflächenbehandlung.cfg'):
super().__init__(config_dir, config_file)
def get_oberflächen(self):
"""hole alle Oberflächen, welche als Sektionen in der config gegeben
sind, also OberflächenbehandlungStahl, OberflächenbehandlungAluminium, ... """
return super().sections()
def get_oberfläche_of_type(self, otype):
"""hole alle möglichen Oberflächen Behandlungsarten zum gegebenen Material
Oberfläche otype
OberflächenbehandlungStahl --> [lackieren, pulverbeschichten, verzinken, vernickeln, brünieren, härten, bürsten, sandstrahlen]
OberflächenbehandlungAluminium
...
"""
return super().get_section_options(otype)
class WerkstoffeConfig(IniBasedConfig):
"""Auslesen der Config zu allen Werkstoffen"""
def __init__(self, config_dir='.', config_file='Werkstoff.cfg'):
super().__init__(config_dir, config_file)
def get_werkstoff_namen(self):
"""hole alle Werkstoffe, welche als Sektionen in der config gegeben sind, also z.B.
Stahl, Aluminium, Kunst-/ Verbundstoff, ...
"""
return super().get_section_options('Werkstofftyp')
def get_werkstoffe_of_name(self, wName):
"""hole alle Werkstoffe unter der gegebenen Sektion
Werkstofftyp wName
Stahl --> [ 1.0035 (S185), 1.0038 (S235JR), 1.0122 (S235JRC), 1.0122+C (S235JRC+C), 1.0330 (DC01), ... ]
Aluminium, ...
"""
return super().get_section_options(wName)
class PfadConfig(IniBasedConfig):
"""Auslesen der Config zu allen Pfaden"""
def __init__(self, config_dir='.', config_file='Zielpfade.cfg'):
super().__init__(config_dir, config_file)
def get(self):
"""hole alle möglichen Bezeichner """
return super().get_section_options("Zielpfade")
class Configs:
"""Sammlung aller Configdateien """
def __init__(self, cfg_dir):
self.Allgemein = AllgemeinConfig(cfg_dir)
self.Bezeichner = BezeichnerConfig(cfg_dir)
self.Farben = FarbenConfig(cfg_dir)
self.Fertigung = FertigungsartConfig(cfg_dir)
self.Hersteller = HerstellerConfig(cfg_dir)
self.Normen = NormenConfig(cfg_dir)
self.Oberfläche = OberflächeConfig(cfg_dir)
self.Pfade = PfadConfig(cfg_dir)
self.Werkstoffe = WerkstoffeConfig(cfg_dir)
self.TOSGruppen = TOSGroupsConfig(cfg_dir)
self.Materialien = MaterialTransConfig(cfg_dir)