diff --git a/lib/compare_lists.py b/lib/compare_lists.py index 04b138b..7cf452e 100644 --- a/lib/compare_lists.py +++ b/lib/compare_lists.py @@ -45,21 +45,18 @@ def create_excel_export(existingIds, sivasData, sivasId, outPath): sortierung.append(create_sorter_string(sivasData, item)) teilenummer.append(item) bezeichnung.append(sivasData[item]['Bezeichnung']) - if existingIds[item] is True: - existing = "VERFÜGBAR" - else: - existing = "IMPORTIEREN" + existing = "VERFÜGBAR" if existingIds[item] is True else False migrationsstatus.append(existing) teilebeurteilung.append('') zusatzbemerkung.append('') data = { - "Sortierung":sortierung, - "Teilenummer":teilenummer, - "Bezeichnung":bezeichnung, - "Migrationsstatus":migrationsstatus, - "Teile Beurteilung":teilebeurteilung, - "Zusatzbemerkung":zusatzbemerkung + "Sortierung": sortierung, + "Teilenummer": teilenummer, + "Bezeichnung": bezeichnung, + "Migrationsstatus": migrationsstatus, + "Teile Beurteilung": teilebeurteilung, + "Zusatzbemerkung": zusatzbemerkung } excel_filename = os.path.join(outPath, sivasId + ".xlsx") @@ -67,50 +64,170 @@ def create_excel_export(existingIds, sivasData, sivasId, outPath): df.to_excel(excel_filename, sheet_name="Ergebnis", index_label="Sortierung") -def compareFiles(rdDataBasePath, jsonPath, sivasId, outPath): - - # Lies die CSV-Datei ein, in der alle RuleDesigner-Nummern aufgelistet sind ein +def read_sivas_nums(jsonPath): + with open(jsonPath, encoding='utf-8') as json_file: + return json.load(json_file) + + +def read_ruledesigner_nums(rdDataBasePath): df = pd.read_csv(rdDataBasePath, sep=';', converters={"Teilenummer":str}) - tn = tuple(df["Teilenummer"]) - - # Lies die JSON-Datei ein, in der alle Sivas-Teilenummern aufgelistet sind, die in Verbindung mit der Migration einer Teilenummer ebenfalls migriert werden müssen - with open(jsonPath, encoding='utf-8') as fp: - sivasData = json.load(fp) + return tuple(df["Teilenummer"]) - # Prüfe, ob die Sivas-Nummern bereits migriert wurden + +def compareFiles(rdDataBasePath, jsonPath, sivasId, outPath): + + # Liest: + # - die CSV-Datei ein, in der alle RuleDesigner-Nummern aufgelistet sind + # - die JSON-Datei ein, in der alle Sivas-Teilenummern, die i.V.m einer Teilenummer ebenfalls migriert werden müssen + ruledesigner_ids = read_ruledesigner_nums(rdDataBasePath) + sivas_ids = read_sivas_nums(jsonPath) + + # Prüft, ob die Sivas-Nummern bereits migriert wurden existingIds = {} - for item in sivasData: - if item in tn: - existingIds[item] = True - else: - existingIds[item] = False + for sivas_id in sivas_ids: + existingIds[sivas_id] = True if sivas_id in ruledesigner_ids else False - # Schreibe eine Excel-Datei mit den korrekten Spalten raus - create_excel_export(existingIds, sivasData, sivasId, outPath) + # Erstellt das work-Verzeichnis, falls dieses noch nicht exisitert + if not os.path.isdir(outPath): + print(f"Fehlendes work-Verzeichnis unter '{outPath}' erstellt") + os.mkdir(outPath) + + # Schreibt eine Excel-Datei mit den korrekten Spalten raus + create_excel_export(existingIds, sivas_ids, sivasId, outPath) + + +def compare_treeview(json_path, ruledesigner_path): + + ruledesigner_nums = read_ruledesigner_nums(ruledesigner_path) + sivas_nums = read_sivas_nums(json_path) + + # Verbinder-Symbole + symbols = { + "pipe": "│ ", + "empty": " ", + "elbow": "└──", + "t": "├──" + } + + # ANSI-Escape-Sequenzen für Einfärbung des Terminals abhängig vom Migrationsstatus. + # Codes, siehe https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 + # BG = Hintergrundfarbe + colors = { + "BG_RED": "\033[41m", + "BG_GREEN": "\033[42m", + "RESET": "\033[0m" + } + + prefix = "" + + # Erstellt dict mit allen Parents (Keys) und weist den Parents deren Kinder als Werte zu. + # Wird später benötigt, um zu bestimmen, ob eine Teilenummer das letzte Kind eines Parents ist d.h. wenn ein Parent z.B. 4 Kinder + # besitzt, ob es sich um das letzte, also 4. Kind handelt. In diesem Fall darf unter der Nummer kein Pipe-Symbol erscheinen + parents_children = {} + for sivas_num in sivas_nums: + parent = sivas_nums[sivas_num]["Parent"] + if parent not in parents_children: + parents_children[parent] = [] + parents_children[parent].append(sivas_num) + + max_tree_depth = max(i["Position"].count(".") for i in sivas_nums.values()) + # Längste Sivas-Teilenummer (weil evtl. >9, wenn mit Underline) + max_sivas_num_len = max([len(i) for i in sivas_nums.keys()]) + max_description_len = max([len(i["Bezeichnung"]) for i in sivas_nums.values()]) + + + # Erstellung des Treeviews mit Spalte "Bezeichnung" + for i, sivas_num in enumerate(sivas_nums): + + # Für Verarbeitung der ANSI-Escape-Sequenzen, + # siehe https://stackoverflow.com/questions/12492810/python-how-can-i-make-the-ansi-escape-codes-to-work-also-in-windows + os.system("color") + + parent = sivas_nums[sivas_num]["Parent"] + description = sivas_nums[sivas_num]["Bezeichnung"] + position = sivas_nums[sivas_num]["Position"] + + # Wird später für Zusammensetzung des Präfix benötigt + if (i + 1) != len(sivas_nums): + next_key = list(sivas_nums)[i + 1] + next_key_pos = sivas_nums[next_key]["Position"] + + # Ermittelt das Level des aktuellen und nächsten Keys bezogen auf die Nummern-Hierarchie + current_key_level = position.count(".") + next_key_level = next_key_pos.count(".") + + # └── als Verbinder, wenn letzter Knotenpunkt, sonst ├── + connector_symbol = symbols['elbow'] if sivas_num == parents_children[parent][-1] else symbols['t'] + + + # Farbliche Terminal-Darstellung abhängig vom Migrationsstatus: grün = migriert, rot = noch nicht migriert + output_color = f"{colors['BG_GREEN']}" if sivas_num in ruledesigner_nums else f"{colors['BG_RED']}" + + # Bei oberster Teilenummer soll kein Verbinder dargestellt werden. + if i == 0: + print(f"{output_color}{parent}{colors['RESET']}") + + tree_str = f"{prefix}{connector_symbol}{output_color}{sivas_num}{colors['RESET']}" + len_tree_str = len(tree_str) - len(output_color) - len(colors['RESET']) + + # Anzahl der Leerzeichen zwischen dem Tree und der Bezeichnungsspalte + pad_tree = ((max_tree_depth * len(symbols['pipe']) + len(symbols['t']) + max_sivas_num_len) - len_tree_str + 2) * " " + # Anzahl der Leerzeichen zwischen dem Tree und der Kommentarspalte + pad_description = (max_description_len - len(description) + 2) * " " + + print(f"{tree_str}{pad_tree}{description}{pad_description}") + + # Nachfolgend wird der Präfix vor der Nummer für den NÄCHSTEN Durchlauf zusammengesetzt + # Der Präfix-Teil setzt sich fallabhängig aus Pipe-Symbolen (| ) und/oder Leerraum ( ) zusammen. + + # Prüft, ob die aktuelle Nummer das letzte Kind des Parents ist + if sivas_num == parents_children[parent][-1]: + + # Prüft, ob das aktuelle Teil auch ein Parent ist. + # Wenn ja, muss unter dem connector-Symbol der aktuellen Nummer (also im nächsten Durchlauf) Leerraum sein + if sivas_num in parents_children: + prefix += symbols["empty"] + + # Baumende auf der aktuellen Ebene ist erreicht. Die nächste Teilenummer (wenn nicht das Gesamtende des Baums erreicht ist) + # liegt unter der aktuellen Ebene. Der bisherige Präfix-String muss folglich gekürzt werden. Die Anzahl der Zeichen, + # die gekürzt werden müssen ergibt sich durch die Anzahl der Ebenen, die zwischen der aktuellen Teilenummer und der darauffolgenden Teilenummer + # liegen und der Länge des Pipe-Symbols, bzw. eines anderen Symbols (alle haben eine Länge von 3) + else: + prefix_trim_len = (next_key_level - current_key_level) * len(symbols["pipe"]) + prefix = prefix[:prefix_trim_len] + else: + # Prüft erneut, ob das aktuelle Teil auch ein Parent ist. + # Wenn ja, muss dieses Mal unter der nächsten Nummer ein Pipe-Symbol kommen + if sivas_num in parents_children: + prefix += symbols["pipe"] if __name__ == '__main__': parser = argparse.ArgumentParser(description='fetches data from sivas or ruledesigner fusion', prog='update_database') + parser.add_argument('-n', '--number', action='store', help='SIVAS-Number') parser.add_argument('-s', '--sivas', action='store', help='fetch data of this id from sivas fo local database folder') parser.add_argument('-d', '--database', action='store', default="RD_Bestandspool.csv", help='local filename of the latest fetched RuleDesigner Database') + parser.add_argument('-t', '--tree', action='store_true', help='displays partnumber hierarchy for given part number in flag -s as treeview. green = migrated, red = not yet migrated') args = parser.parse_args() + # fetching the names of all existing cad files from the local file + dataDir = os.environ.get('CREMIG_DATA') + rdDataBasePath = os.path.join(dataDir, args.database) + # get the path to ask for further json files + exePath = os.environ.get('SIVAS_DATABASE_QUERY') + # in case the infos for this cad file are already there + sivasId = str(args.sivas) + jsonPath = os.path.join(dataDir, sivasId + ".json") + # there the results are written + outPath = os.environ.get('CREMIG_WORK') + + sivas_id = str(args.number) + json_path = os.path.join(dataDir, sivas_id + ".json") if args.sivas: - # fetching the names of all existing cad files from the local file - dataDir = os.environ.get('CREMIG_DATA') - rdDataBasePath = os.path.join(dataDir, args.database) - # get the path to ask for further json files - exePath = os.environ.get('SIVAS_DATABASE_QUERY') - # in case the infos for this cad file are already there - sivasId = str(args.sivas) - jsonPath = os.path.join(dataDir, sivasId + ".json") - # there the results are written - outPath = os.environ.get('CREMIG_WORK') - compareFiles(rdDataBasePath, jsonPath, sivasId, outPath) - - + if args.tree: + compare_treeview(json_path, rdDataBasePath)