From 5f3615548b3d02d55504b2bfb0225f57188e810c Mon Sep 17 00:00:00 2001 From: Simon Steuer Date: Sun, 9 Mar 2025 21:22:15 +0100 Subject: [PATCH] =?UTF-8?q?Treeview=20angepasst,=20damit=20die=20Bezeichnu?= =?UTF-8?q?ng=20der=20obersten=20Baugruppe=20angezeigt=20wird,=20da=20dies?= =?UTF-8?q?=20bisher=20nicht=20der=20Fall=20war.=20Da=20die=20Bezeichnung?= =?UTF-8?q?=20nicht=20in=20der=20Baugruppen-JSON=20aufgef=C3=BChrt=20ist,?= =?UTF-8?q?=20ist=20es=20erforderlich=20die=20CSV=20mit=20den=20Teilestamm?= =?UTF-8?q?-Metadaten=20zur=20Baugruppennummer=20herunterzuladen.=20Damit?= =?UTF-8?q?=20dies=20m=C3=B6glich=20ist,=20wurde=20wurde=20in=20manage=5Fc?= =?UTF-8?q?onfigs.py=20eine=20neue=20Subclass=20'TeilestammMetadata'=20erg?= =?UTF-8?q?=C3=A4nzt.=20In=20compare=5Flists.py=20wurden=20dabei=20weitere?= =?UTF-8?q?=20Flags=20erg=C3=A4nzt,=20mit=20denen=20gesteuert=20werden=20k?= =?UTF-8?q?ann,=20ob=20die=20Bezeichnung=20aus=20der=20lokalen=20CSV-Datei?= =?UTF-8?q?=20gezogen=20werden=20sollen,=20oder=20ob=20die=20CSV=20mit=20j?= =?UTF-8?q?edem=20Programmlauf=20neu=20erstellt=20wird.=20Defaultm=C3=A4?= =?UTF-8?q?=C3=9Fig=20wird=20sie=20mit=20jedem=20Programmlauf=20neu=20erst?= =?UTF-8?q?ellt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/compare_lists.py | 98 ++++++++++++++++++++++++++++++++---------- lib/manage_configs.py | 42 ++++++++++++++---- lib/update_database.py | 4 +- 3 files changed, 111 insertions(+), 33 deletions(-) diff --git a/lib/compare_lists.py b/lib/compare_lists.py index c21dae0..c650e8d 100644 --- a/lib/compare_lists.py +++ b/lib/compare_lists.py @@ -1,4 +1,4 @@ -from manage_configs import SivasAssemblyData, RuleDesignerDatabaseConfig, MigrationStatus +from manage_configs import SivasAssemblyData, RuleDesignerDatabaseConfig, MigrationStatus, TeilestammMetadata import argparse import os @@ -13,7 +13,14 @@ Dieses Programm kann: 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_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) @@ -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_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._migration_status = None + self._longest_entry = None @property def migration_status(self): 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 + + @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): 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""" - background_color = self._set_text_background_color(self.asm_id) - print(f"{background_color}{self.asm_id}{Treeview.console_colors['RESET']}") + id = self.asm_id + 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=""): @@ -173,7 +212,7 @@ class Treeview: if parent_id is None: 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] for index, child_id in enumerate(children_ids): @@ -194,7 +233,7 @@ class Treeview: amount_migrated_ids = len(self.migration_status.green_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 border_padding = "".ljust(column_width, "─") @@ -215,15 +254,9 @@ class Treeview: """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) - description = self.asm_data.get_bom_property_values("Bezeichnung")[child_id] + text_before_description = f"{prefix}{connector}{color}{child_id}{Treeview.console_colors['RESET']}".ljust(self.longest_entry) + description = self.asm_bom_data.get_bom_property_values("Bezeichnung")[child_id] 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('-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('-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('-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('-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('-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('-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('-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() 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: - 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() 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() else: parser.print_help() \ No newline at end of file diff --git a/lib/manage_configs.py b/lib/manage_configs.py index 6e5ff8c..cbf8d61 100644 --- a/lib/manage_configs.py +++ b/lib/manage_configs.py @@ -27,8 +27,10 @@ class JsonBasedConfig: 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""" + self._config_dir = config_dir + self._config_file = config_file self._config_path = os.path.join(config_dir, config_file) def load_csv_from_file(self): @@ -62,9 +64,6 @@ class SivasAssemblyData(JsonBasedConfig): """Liest die Baugruppen-JSON ein und gibt deren Informationen zurück""" 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: self.create_asm_json() @@ -182,8 +181,6 @@ class RuleDesignerDatabaseConfig(CSVBasedConfig): @property def ids(self): 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: self.copy_csv_from_server() @@ -232,6 +229,37 @@ class MigrationStatus: 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): """Auslesen der Config Angaben zu den TOS Gruppen""" @@ -595,5 +623,3 @@ class Configs: lines = f.read().splitlines() self._blacklist = lines return self._blacklist - - diff --git a/lib/update_database.py b/lib/update_database.py index 179364a..db4da19 100644 --- a/lib/update_database.py +++ b/lib/update_database.py @@ -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") 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_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") 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""" sivas_teilestamm_exe = os.environ.get('SIVAS_TEILESTAMM')