Treeview angepasst, damit die Bezeichnung der obersten Baugruppe angezeigt wird, da dies bisher nicht der Fall war. Da die Bezeichnung nicht in der Baugruppen-JSON aufgeführt ist, ist es erforderlich die CSV mit den Teilestamm-Metadaten zur Baugruppennummer herunterzuladen. Damit dies möglich ist, wurde wurde in manage_configs.py eine neue Subclass 'TeilestammMetadata' ergänzt. In compare_lists.py wurden dabei weitere Flags ergänzt, mit denen gesteuert werden kann, ob die Bezeichnung aus der lokalen CSV-Datei gezogen werden sollen, oder ob die CSV mit jedem Programmlauf neu erstellt wird. Defaultmäßig wird sie mit jedem Programmlauf neu erstellt

This commit is contained in:
2025-03-09 21:22:15 +01:00
parent 4f740e81ad
commit 5f3615548b
3 changed files with 111 additions and 33 deletions
+75 -23
View File
@@ -1,4 +1,4 @@
from manage_configs import SivasAssemblyData, RuleDesignerDatabaseConfig, MigrationStatus from manage_configs import SivasAssemblyData, RuleDesignerDatabaseConfig, MigrationStatus, TeilestammMetadata
import argparse import argparse
import os import os
@@ -13,7 +13,14 @@ Dieses Programm kann:
class ExcelExport: class ExcelExport:
def __init__(self, asm_id, asm_json_dir, asm_json_filename, rd_dbase_dir, rd_dbase_filename, load_local_asm_json, load_local_rd_dbase): def __init__(self,
asm_id,
asm_json_dir,
asm_json_filename,
rd_dbase_dir,
rd_dbase_filename,
load_local_asm_json,
load_local_rd_dbase):
self.asm_id = asm_id self.asm_id = asm_id
self.asm_data = SivasAssemblyData(asm_json_dir, asm_json_filename, load_local_asm_json) self.asm_data = SivasAssemblyData(asm_json_dir, asm_json_filename, load_local_asm_json)
self.rd_data = RuleDesignerDatabaseConfig(rd_dbase_dir, rd_dbase_filename, load_local_rd_dbase) self.rd_data = RuleDesignerDatabaseConfig(rd_dbase_dir, rd_dbase_filename, load_local_rd_dbase)
@@ -142,17 +149,44 @@ class Treeview:
} }
def __init__(self, asm_id, asm_json_dir, asm_json_filename, rd_dbase_dir, rd_dbase_filename, load_local_asm_json, load_local_rd_dbase): def __init__(self,
asm_id,
asm_json_dir,
asm_json_filename,
asm_csv_dir,
asm_csv_filename,
rd_dbase_dir,
rd_dbase_filename,
load_local_asm_json,
load_local_asm_csv,
load_local_rd_dbase):
self.asm_id = asm_id self.asm_id = asm_id
self.asm_data = SivasAssemblyData(asm_json_dir, asm_json_filename, load_local_asm_json) self.asm_bom_data = SivasAssemblyData(asm_json_dir, asm_json_filename, load_local_asm_json)
self.asm_metadata = TeilestammMetadata(asm_csv_dir, asm_csv_filename, load_local_asm_csv)
self.rd_data = RuleDesignerDatabaseConfig(rd_dbase_dir, rd_dbase_filename, load_local_rd_dbase) self.rd_data = RuleDesignerDatabaseConfig(rd_dbase_dir, rd_dbase_filename, load_local_rd_dbase)
self._migration_status = None self._migration_status = None
self._longest_entry = None
@property @property
def migration_status(self): def migration_status(self):
if self._migration_status is None: if self._migration_status is None:
self._migration_status = MigrationStatus(self.asm_data.child_ids, self.rd_data.ids) self._migration_status = MigrationStatus(self.asm_bom_data.child_ids, self.rd_data.ids)
return self._migration_status return self._migration_status
@property
def longest_entry(self):
# Längster Eintrag wird zur Bestimmung der Leerraum-Breite zwischen Teilenummer und Bezeichnung im Baum benötigt
# Es hätte für die Berechnung sowohl ein anderer Farbcode, als auch ein anderes Verbindersymbol angegeben werden können,
# da die Anderen gleich lang wie die Angegebenen sind
if self._longest_entry is None:
self._longest_entry = self.asm_bom_data.max_bom_depth * len(Treeview.prefix_types["PIPE"]) \
+ len(self.asm_bom_data.longest_child_id) \
+ len(Treeview.connector_symbols["T"]) \
+ len(Treeview.console_colors["RESET"]) \
+ len(Treeview.console_colors["RED"])
return self._longest_entry
def generate_tree(self): def generate_tree(self):
self._tree_header() self._tree_header()
@@ -163,8 +197,13 @@ class Treeview:
"""Prints root number (number for which the user wants to see the tree) to the console""" """Prints root number (number for which the user wants to see the tree) to the console"""
background_color = self._set_text_background_color(self.asm_id) id = self.asm_id
print(f"{background_color}{self.asm_id}{Treeview.console_colors['RESET']}") color = self._set_text_background_color(id)
text_before_description = f"{color}{id}{Treeview.console_colors['RESET']}".ljust(self.longest_entry)
description = self.asm_metadata.get_property_values("Bezeichnung1")
print(f"{text_before_description} {description}")
def _tree_body(self, parent_id=None, prefix=""): def _tree_body(self, parent_id=None, prefix=""):
@@ -173,7 +212,7 @@ class Treeview:
if parent_id is None: if parent_id is None:
parent_id = self.asm_id parent_id = self.asm_id
parent_children = self.asm_data.parent_children parent_children = self.asm_bom_data.parent_children
children_ids = parent_children[parent_id] children_ids = parent_children[parent_id]
for index, child_id in enumerate(children_ids): for index, child_id in enumerate(children_ids):
@@ -194,7 +233,7 @@ class Treeview:
amount_migrated_ids = len(self.migration_status.green_ids) amount_migrated_ids = len(self.migration_status.green_ids)
amount_unmigrated_ids = len(self.migration_status.red_ids) amount_unmigrated_ids = len(self.migration_status.red_ids)
progress = int(amount_migrated_ids / len(self.asm_data.child_ids) * 100) progress = int(amount_migrated_ids / len(self.asm_bom_data.child_ids) * 100)
column_width = 5 column_width = 5
border_padding = "".ljust(column_width, "") border_padding = "".ljust(column_width, "")
@@ -215,15 +254,9 @@ class Treeview:
"""Prints tree entry""" """Prints tree entry"""
# Necessary for calculating the padding-width after the partnumber
longest_entry = self.asm_data.max_bom_depth * len(Treeview.prefix_types["PIPE"]) \
+ len(connector) \
+ len(self.asm_data.longest_child_id) \
+ len(Treeview.console_colors["RESET"]) \
+ len(Treeview.console_colors["RED"]) # other color codes (ansi escape sequences) could have been used instead since they all have the same length
text_before_description = f"{prefix}{connector}{color}{child_id}{Treeview.console_colors['RESET']}".ljust(longest_entry) text_before_description = f"{prefix}{connector}{color}{child_id}{Treeview.console_colors['RESET']}".ljust(self.longest_entry)
description = self.asm_data.get_bom_property_values("Bezeichnung")[child_id] description = self.asm_bom_data.get_bom_property_values("Bezeichnung")[child_id]
print(f"{text_before_description} {description}") print(f"{text_before_description} {description}")
@@ -266,20 +299,39 @@ if __name__ == '__main__':
parser.add_argument('-e', '--excel', action='store_true', help='Exportiert Baugruppenstruktur als Excel-Datei mit farbiger Hervorhebung der Stücklistenpositionen, in Abhängigkeit vom Migrationsstatus') parser.add_argument('-e', '--excel', action='store_true', help='Exportiert Baugruppenstruktur als Excel-Datei mit farbiger Hervorhebung der Stücklistenpositionen, in Abhängigkeit vom Migrationsstatus')
parser.add_argument('-rddir', '--rd_dbase_dir',required=False, action='store', default=os.environ.get("CREMIG_DATA"), help='Legt das Verzeichnis fest, in dem die CSV-Datei liegt, in der die RuleDesigner-Teilenummern aufgelistet sind. Default: Umgebungsvariable %CREMIG_DATA%') parser.add_argument('-rddir', '--rd_dbase_dir',required=False, action='store', default=os.environ.get("CREMIG_DATA"), help='Legt das Verzeichnis fest, in dem die CSV-Datei liegt, in der die RuleDesigner-Teilenummern aufgelistet sind. Default: Umgebungsvariable %CREMIG_DATA%')
parser.add_argument('-rdfn', '--rd_dbase_filename', required=False, action='store', default=os.environ.get("RD_DATABASE_NAME"), help='Name der CSV-Datei, in der die RuleDesigner-Teilenummern aufgelistet sind. Default: Umgebungsvariable %RD_DATABASE_NAME%') parser.add_argument('-rdfn', '--rd_dbase_filename', required=False, action='store', default=os.environ.get("RD_DATABASE_NAME"), help='Name der CSV-Datei, in der die RuleDesigner-Teilenummern aufgelistet sind. Default: Umgebungsvariable %RD_DATABASE_NAME%')
parser.add_argument('-asmdir', '--asm_json_dir', required=False, action='store', default=os.environ.get("CREMIG_DATA"), help='Legt das Verzeichnis fest, JSON-Datei mit den Baugruppeninformationen liegen soll. Default: Umgebungsvariable %CREMIG_DATA%', metavar='filename') parser.add_argument('-asmjdir', '--asm_json_dir', required=False, action='store', default=os.environ.get("CREMIG_DATA"), help='Legt das Verzeichnis fest, in dem die JSON-Datei mit den Baugruppeninformationen liegt. Default: Umgebungsvariable %CREMIG_DATA%', metavar='directory')
parser.add_argument('-ljson', '--local_asm_json', required=False, action='store_true', default=False, help='Legt fest, ob die JSON der Baugruppe bei jedem Programmlauf neu geladen wird, oder ob die lokale Datei eingelesen werden soll. Default: Neu laden bei jedem Programmlauf') parser.add_argument('-asmcdir', '--asm_csv_dir', required=False, action='store', default=os.environ.get("CREMIG_DATA"), help='Legt das Verzeichnis fest, in dem die CSV-Datei mit den Teilestamm-Metadaten der Baugruppennummer liegt. Default: Umgebungsvariable %CREMIG_DATA%', metavar='directory')
parser.add_argument('-lcsv', '--local_rd_csv', required=False, action='store_true', default=False, help='Legt fest, ob die CSV, die die RuleDesigner-Teilenummern enthält (RD-Teilenummer.csv) bei jedem Programmlauf vom Server in das lokale Verzeichnis kopiert werden soll. Default: Kopieren bei jedem Programmlauf') parser.add_argument('-lasmjson', '--local_asm_json', required=False, action='store_true', default=False, help='Legt fest, ob die JSON der Baugruppe bei jedem Programmlauf neu geladen wird, oder ob die lokale Datei eingelesen werden soll. Default: Neu laden bei jedem Programmlauf')
parser.add_argument('-lasmcsv', '--local_asm_csv', required=False, action='store_true', default=False, help='Legt fest, ob die Metadaten-CSV der Baugruppennummer bei jedem Programmlauf neu geladen wird. Default: Neu laden bei jedem Programmlauf')
parser.add_argument('-lrdcsv', '--local_rd_csv', required=False, action='store_true', default=False, help='Legt fest, ob die CSV, die die RuleDesigner-Teilenummern enthält (RD-Teilenummer.csv) bei jedem Programmlauf vom Server in das lokale Verzeichnis kopiert werden soll. Default: Kopieren bei jedem Programmlauf')
args = parser.parse_args() args = parser.parse_args()
asm_id = args.sivasid.lstrip() asm_id = args.sivasid.lstrip()
asm_json_filename = f'{asm_id}.json' asm_bom_filename = f'{asm_id}.json'
asm_metadata_filename = f'{asm_id}.csv'
if args.excel: if args.excel:
excel_comparison = ExcelExport(asm_id, args.asm_json_dir, asm_json_filename, args.rd_dbase_dir, args.rd_dbase_filename, args.local_asm_json, args.local_rd_csv) excel_comparison = ExcelExport(asm_id=asm_id,
asm_json_dir=args.asm_json_dir,
asm_json_filename=asm_bom_filename,
rd_dbase_dir=args.rd_dbase_dir,
rd_dbase_filename=args.rd_dbase_filename,
load_local_asm_json=args.local_asm_json,
load_local_rd_dbase=args.local_rd_csv)
excel_comparison.write_excel() excel_comparison.write_excel()
elif args.tree: elif args.tree:
treeview_comparison = Treeview(asm_id, args.asm_json_dir, asm_json_filename, args.rd_dbase_dir, args.rd_dbase_filename, args.local_asm_json, args.local_rd_csv) treeview_comparison = Treeview(asm_id=asm_id,
asm_json_dir=args.asm_json_dir,
asm_json_filename=asm_bom_filename,
asm_csv_dir=args.asm_csv_dir,
asm_csv_filename=asm_metadata_filename,
rd_dbase_dir=args.rd_dbase_dir,
rd_dbase_filename=args.rd_dbase_filename,
load_local_asm_json=args.local_asm_json,
load_local_asm_csv=args.local_asm_csv,
load_local_rd_dbase=args.local_rd_csv)
treeview_comparison.generate_tree() treeview_comparison.generate_tree()
else: else:
parser.print_help() parser.print_help()
+34 -8
View File
@@ -27,8 +27,10 @@ class JsonBasedConfig:
class CSVBasedConfig: class CSVBasedConfig:
def __init__(self, config_dir, config_file): def __init__(self, config_dir: str, config_file: str):
"""merke dir den Pfad zu den Configdateien""" """merke dir den Pfad zu den Configdateien"""
self._config_dir = config_dir
self._config_file = config_file
self._config_path = os.path.join(config_dir, config_file) self._config_path = os.path.join(config_dir, config_file)
def load_csv_from_file(self): def load_csv_from_file(self):
@@ -62,9 +64,6 @@ class SivasAssemblyData(JsonBasedConfig):
"""Liest die Baugruppen-JSON ein und gibt deren Informationen zurück""" """Liest die Baugruppen-JSON ein und gibt deren Informationen zurück"""
if self._asm_data is None: if self._asm_data is None:
if not os.path.isfile(self._config_path):
raise FileNotFoundError(f"{self._config_path} existiert nicht. Bitte prüfen, ob die Nummer richtig geschrieben wurde.")
if not self._load_local_json: if not self._load_local_json:
self.create_asm_json() self.create_asm_json()
@@ -182,8 +181,6 @@ class RuleDesignerDatabaseConfig(CSVBasedConfig):
@property @property
def ids(self): def ids(self):
if self._ids is None: if self._ids is None:
if not os.path.isfile(self._config_path):
raise FileNotFoundError(f"{self._config_path} existiert nicht. Bitte Batchdatei unter 'bin/01_get_rd_dbase.bat' ausführen!")
if not self._load_local_csv: if not self._load_local_csv:
self.copy_csv_from_server() self.copy_csv_from_server()
@@ -232,6 +229,37 @@ class MigrationStatus:
return self._red_ids return self._red_ids
class TeilestammMetadata(CSVBasedConfig):
def __init__(self, config_dir, config_file, load_local_csv=False):
super().__init__(config_dir, config_file)
self._load_local_csv = load_local_csv
self._csv_data = None
@staticmethod
def create_csvs(ids):
"""Erstellt CSV-Datei(en) mit den Teilestamminformationen zu der / den angegebenen Teilenummer(n)"""
try:
update_database.get_sivas_teilestamm(ids)
except:
raise ValueError("CSV(s) konnte nicht generiert werden!")
@property
def csv_data(self):
"""Gibt CSV-Daten als Dataframe zurück"""
if self._csv_data is None:
if not self._load_local_csv:
self.create_csvs(self._config_file.removesuffix(".csv"))
self._csv_data = self.load_csv_from_file()
return self._csv_data
def get_property_values(self, property_name) -> str:
"""Gibt Wert aus der CSV für den angegebenen Spaltennamen zurück"""
ret = self.csv_data[property_name][0]
return ret
class TOSGroupsConfig(JsonBasedConfig): class TOSGroupsConfig(JsonBasedConfig):
"""Auslesen der Config Angaben zu den TOS Gruppen""" """Auslesen der Config Angaben zu den TOS Gruppen"""
@@ -595,5 +623,3 @@ class Configs:
lines = f.read().splitlines() lines = f.read().splitlines()
self._blacklist = lines self._blacklist = lines
return self._blacklist return self._blacklist
+2 -2
View File
@@ -63,7 +63,7 @@ def get_rd_dbase(out_dir=os.environ.get('CREMIG_DATA')):
print(f"File with ruldesigner-partnumbers ${csv_source_path} doesn't exist. Check VPN connection and %RD_DATABASE_SOURCE% + %RD_DATABASE_NAME% in cremig_setenv.bat") print(f"File with ruldesigner-partnumbers ${csv_source_path} doesn't exist. Check VPN connection and %RD_DATABASE_SOURCE% + %RD_DATABASE_NAME% in cremig_setenv.bat")
sys.exit(0) sys.exit(0)
def get_sivas_dbase(sivas_id, out_dir): def get_sivas_dbase(sivas_id, out_dir=os.environ.get('CREMIG_DATA')):
sivas_exe_path = os.environ.get('SIVAS_DATABASE_QUERY') sivas_exe_path = os.environ.get('SIVAS_DATABASE_QUERY')
sivas_id = sivas_id.replace(".json", "") sivas_id = sivas_id.replace(".json", "")
@@ -86,7 +86,7 @@ def get_sivas_dbase(sivas_id, out_dir):
print(f"Can't execute SIVAS-database query. Check VPN connection and check the path of %SIVAS_DATABASE_QUERY% in cremig_setenv.bat") print(f"Can't execute SIVAS-database query. Check VPN connection and check the path of %SIVAS_DATABASE_QUERY% in cremig_setenv.bat")
return False return False
def get_sivas_teilestamm(sivas_ids, out_dir, overwrite=True): def get_sivas_teilestamm(sivas_ids, out_dir=os.environ.get('CREMIG_DATA'), overwrite=True):
"""call the sivas exe for getting all csv files with the item data for the given sivas ids""" """call the sivas exe for getting all csv files with the item data for the given sivas ids"""
sivas_teilestamm_exe = os.environ.get('SIVAS_TEILESTAMM') sivas_teilestamm_exe = os.environ.get('SIVAS_TEILESTAMM')