Klasse MigrationsStatus entfernt und stattdessen auf gleichnamige Klasse in manage_configs.py referenziert, um Redundanzen zu vermeiden. Zudem Flags zur Definierung des JSON-Verzeichnises, des Namens und Verzeichnisses für die CSV mit den RuleDesigner-Teilenummern ergänzt. Außerdem Flags vorgesehen, mit denen gesteuert werden kann, ob die die lokalen JSON-Dateien sowie die lokale CSV der RuleDesigner-Teilenummern verwendet werden soll. Defaultmäßig sollen bei jedem Programmlauf nun immer die neusten Daten vom Server geholt werden, bzw. immer eine JSON generiert werden, da bisher sonst nicht immer die Aktualität der dargestellten Daten sichergestellt war.
This commit is contained in:
+52
-165
@@ -1,8 +1,5 @@
|
||||
from manage_configs import SivasAssemblyData, RuleDesignerDatabaseConfig
|
||||
from manage_configs import SivasAssemblyData, RuleDesignerDatabaseConfig, MigrationStatus
|
||||
|
||||
import warnings
|
||||
#Wegen Pandas Deprecation eingebaut
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
import argparse
|
||||
import os
|
||||
import openpyxl
|
||||
@@ -14,92 +11,20 @@ Dieses Programm kann:
|
||||
- die oben beschriebene Darstellung in einer Excel-Datei auch als Treeview auf der Kommandozeile darstellen
|
||||
"""
|
||||
|
||||
class MigrationStatus:
|
||||
|
||||
def __init__(self, asm_child_ids: tuple, rd_ids: tuple):
|
||||
self.asm_child_ids = asm_child_ids
|
||||
self.rd_ids = rd_ids
|
||||
self._overview = None
|
||||
self._green_ids = None
|
||||
self._yellow_ids = None
|
||||
self._red_ids = None
|
||||
|
||||
@property
|
||||
def overview(self) -> dict:
|
||||
"""Returns dict with children of the given assembly and their migration status (True = migration complete, False = migration pending)"""
|
||||
|
||||
if self._overview is None:
|
||||
self._overview = {}
|
||||
for child_id in self.asm_child_ids:
|
||||
self._overview[child_id] = True if child_id in self.rd_ids else False
|
||||
return self._overview
|
||||
|
||||
@property
|
||||
def green_ids(self) -> list:
|
||||
|
||||
"""Returns migrated child-ids of given assembly"""
|
||||
|
||||
if self._green_ids is None:
|
||||
self._green_ids = [id for id, status in self.overview.items() if status is True]
|
||||
return self._green_ids
|
||||
|
||||
@property
|
||||
def red_ids(self) -> list:
|
||||
|
||||
"""Returns unmigrated child-ids of given assembly"""
|
||||
|
||||
if self._red_ids is None:
|
||||
self._red_ids = [id for id, status in self.overview.items() if status is False]
|
||||
return self._red_ids
|
||||
|
||||
@property
|
||||
def yellow_ids(self) -> list:
|
||||
"""Returns migrated child-ids of given assembly, which prevent the asm from being released, because they are currently being modified"""
|
||||
pass
|
||||
|
||||
|
||||
class ExcelExport:
|
||||
|
||||
def __init__(self, asm_id, asm_json_dir, asm_json_filename, rd_dbase_dir, rd_dbase_filename):
|
||||
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)
|
||||
self._rd_dbase = RuleDesignerDatabaseConfig(rd_dbase_dir, rd_dbase_filename)
|
||||
self._asm_child_ids = None
|
||||
self._rd_ids = None
|
||||
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._migration_status = None
|
||||
self._parent_children = None
|
||||
self._id_descriptions = None
|
||||
|
||||
@property
|
||||
def rd_ids(self):
|
||||
if self._rd_ids is None:
|
||||
self._rd_ids = self._rd_dbase.get_ruledesigner_ids()
|
||||
return self._rd_ids
|
||||
|
||||
@property
|
||||
def migration_status(self):
|
||||
if self._migration_status is None:
|
||||
self._migration_status = MigrationStatus(self.asm_child_ids, self.rd_ids)
|
||||
self._migration_status = MigrationStatus(self.asm_data.child_ids, self.rd_data.ids)
|
||||
return self._migration_status
|
||||
|
||||
@property
|
||||
def parent_children(self) -> dict:
|
||||
if self._parent_children is None:
|
||||
self._parent_children = self._asm_data.get_parent_children()
|
||||
return self._parent_children
|
||||
|
||||
@property
|
||||
def id_descriptions(self):
|
||||
if self._id_descriptions is None:
|
||||
self._id_descriptions = self._asm_data.get_bom_property_values("Bezeichnung")
|
||||
return self._id_descriptions
|
||||
|
||||
@property
|
||||
def asm_child_ids(self):
|
||||
if self._asm_child_ids is None:
|
||||
self._asm_child_ids = self._asm_data.get_assembly_ids()
|
||||
return self._asm_child_ids
|
||||
|
||||
def write_excel(self):
|
||||
|
||||
data = self._create_excel_data()
|
||||
@@ -110,19 +35,19 @@ class ExcelExport:
|
||||
workbook = openpyxl.load_workbook(workbook_path, keep_vba=True)
|
||||
worksheet = workbook.active
|
||||
|
||||
# Headers
|
||||
# Spaltenüberschriften
|
||||
for header, column in zip(data, worksheet.iter_cols(min_row=1, max_col=len(data), max_row=1)):
|
||||
for cell in column:
|
||||
cell.value = header
|
||||
|
||||
# Values below Headers
|
||||
# Werte unter Spaltenüberschriften
|
||||
for value, column in zip(data.values(), worksheet.iter_cols(min_row=2, max_col=len(data), max_row=len(data["Sortierung"])+1)):
|
||||
for cell, element in zip(column, value):
|
||||
cell.value = element
|
||||
|
||||
# Renaming the sheet is necessary, because that triggers the execution of the VBA-Macro
|
||||
# The renaming must be performed after inserting the data into the spreadsheet, because the conditional formatting, which is part of the vba-macro, can't be performed
|
||||
# when no data is in the spreadsheet
|
||||
# Umbenennung des Blattes erforderlich, da die Umbenennung die Ausführung des VBA-Makros triggert.
|
||||
# Die Umbenennung muss NACH dem Einfügen der Daten in das Blatt erfolgen, da die bedingte Formatierung,
|
||||
# die ebenfalls Bestandteil des Makros ist, nicht ausgeführt werden kann, solange sich keine Daten im Blatt befinden
|
||||
worksheet.title = "Ergebnis"
|
||||
|
||||
workbook.save(out_path)
|
||||
@@ -130,32 +55,32 @@ class ExcelExport:
|
||||
|
||||
def _create_excel_data(self) -> dict:
|
||||
|
||||
# Columns in excel-spreadsheet: Sortierung, Teilenummer, Bezeichnung, Bestandsabfrage, Teilebeurteilung, Zusatzbemerkung
|
||||
# Sortierung: Minus-separated hierarchy of the partnumber in the BOM structure
|
||||
# Teilenummer: SIVAS-partnumber
|
||||
# Bezeichnung: SIVAS-description of the partnumber
|
||||
# Migrationsstatus: "IMPORTIEREN" = partnumber hasn't been migrated yet, "VERFÜGBAR" = partnumber has been migrated
|
||||
# All the other columns remain empty, because their values are entered in the excel spreadsheet
|
||||
# Spalten im Arbeitsblatt: Sortierung, Teilenummer, Bezeichnung, Bestandsabfrage, Teilebeurteilung, Zusatzbemerkung
|
||||
# Sortierung: Minus-separierte Hierarchie der Teilenummern in der Stücklistenstruktur
|
||||
# Teilenummer: SIVAS-Teilenummer
|
||||
# Bezeichnung: SIVAS-Bezeichnung der Teilenummer
|
||||
# Migrationsstatus: "IMPORTIEREN" = Migration ausstehend, "VERFÜGBAR" = Migration abgeschlossen
|
||||
# Alle weiteren Spalten bleiben leer, da sie händisch vom User zu befüllen sind
|
||||
|
||||
sorting = self._create_sorting_string()
|
||||
migrated = self.migration_status.green_ids
|
||||
descriptions = self.id_descriptions
|
||||
migrated_ids = self.migration_status.green_ids
|
||||
descriptions = self.asm_data.get_bom_property_values("Bezeichnung")
|
||||
|
||||
prt_num = []
|
||||
description = []
|
||||
migration_status = []
|
||||
evaluation = []
|
||||
assessment = []
|
||||
comment = []
|
||||
|
||||
for partnumber_string in sorting:
|
||||
partnumber = partnumber_string.split("-")[-1]
|
||||
prt_num.append(partnumber)
|
||||
if partnumber not in descriptions:
|
||||
for id_str in sorting:
|
||||
id = id_str.split("-")[-1]
|
||||
prt_num.append(id)
|
||||
if id not in descriptions:
|
||||
description.append("")
|
||||
else:
|
||||
description.append(descriptions[partnumber])
|
||||
migration_status.append("VERFÜGBAR" if partnumber in migrated else "IMPORTIEREN")
|
||||
evaluation.append('')
|
||||
description.append(descriptions[id])
|
||||
migration_status.append("VERFÜGBAR" if id in migrated_ids else "IMPORTIEREN")
|
||||
assessment.append('')
|
||||
comment.append('')
|
||||
|
||||
data = {
|
||||
@@ -163,7 +88,7 @@ class ExcelExport:
|
||||
"Teilenummer": prt_num,
|
||||
"Bezeichnung": description,
|
||||
"Migrationsstatus": migration_status,
|
||||
"Beurteilung": evaluation,
|
||||
"Beurteilung": assessment,
|
||||
"Bemerkung": comment
|
||||
}
|
||||
return data
|
||||
@@ -186,13 +111,14 @@ class ExcelExport:
|
||||
if sorting_strings is None:
|
||||
sorting_strings = [parent]
|
||||
|
||||
children = self.parent_children[parent]
|
||||
parent_children = self.asm_data.parent_children
|
||||
children = parent_children[parent]
|
||||
|
||||
for child in children:
|
||||
|
||||
sorting_strings.append(f"{previous_parents}-{parent}-{child}".lstrip("-"))
|
||||
|
||||
if child in self.parent_children:
|
||||
if child in parent_children:
|
||||
self._create_sorting_string(parent=child, previous_parents=previous_parents + f"-{parent}", sorting_strings=sorting_strings)
|
||||
|
||||
return sorting_strings
|
||||
@@ -216,59 +142,17 @@ class Treeview:
|
||||
}
|
||||
|
||||
|
||||
def __init__(self, asm_id, asm_json_dir, asm_json_filename, rd_dbase_dir, rd_dbase_filename):
|
||||
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)
|
||||
self._rd_dbase = RuleDesignerDatabaseConfig(rd_dbase_dir, rd_dbase_filename)
|
||||
self._asm_child_ids = None
|
||||
self._rd_ids = None
|
||||
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._migration_status = None
|
||||
self._parent_children = None
|
||||
self._id_descriptions = None
|
||||
self._max_tree_depth = None
|
||||
self._longest_partnumber = None
|
||||
|
||||
@property
|
||||
def asm_child_ids(self):
|
||||
if self._asm_child_ids is None:
|
||||
self._asm_child_ids = self._asm_data.get_assembly_ids()
|
||||
return self._asm_child_ids
|
||||
|
||||
@property
|
||||
def rd_ids(self):
|
||||
if self._rd_ids is None:
|
||||
self._rd_ids = self._rd_dbase.get_ruledesigner_ids()
|
||||
return self._rd_ids
|
||||
|
||||
@property
|
||||
def migration_status(self):
|
||||
if self._migration_status is None:
|
||||
self._migration_status = MigrationStatus(self.asm_child_ids, self.rd_ids)
|
||||
self._migration_status = MigrationStatus(self.asm_data.child_ids, self.rd_data.ids)
|
||||
return self._migration_status
|
||||
|
||||
@property
|
||||
def parent_children(self) -> dict:
|
||||
if self._parent_children is None:
|
||||
self._parent_children = self._asm_data.get_parent_children()
|
||||
return self._parent_children
|
||||
|
||||
@property
|
||||
def id_descriptions(self):
|
||||
if self._id_descriptions is None:
|
||||
self._id_descriptions = self._asm_data.get_bom_property_values("Bezeichnung")
|
||||
return self._id_descriptions
|
||||
|
||||
@property
|
||||
def max_tree_depth(self):
|
||||
if self._max_tree_depth is None:
|
||||
self._max_tree_depth = self._asm_data.get_max_bom_depth()
|
||||
return self._max_tree_depth
|
||||
|
||||
@property
|
||||
def longest_partnumber(self):
|
||||
if self._longest_partnumber is None:
|
||||
self._longest_partnumber = self._asm_data.get_longest_assembly_id()
|
||||
return self._longest_partnumber
|
||||
|
||||
def generate_tree(self):
|
||||
self._tree_header()
|
||||
@@ -289,7 +173,8 @@ class Treeview:
|
||||
if parent_id is None:
|
||||
parent_id = self.asm_id
|
||||
|
||||
children_ids = self.parent_children[parent_id]
|
||||
parent_children = self.asm_data.parent_children
|
||||
children_ids = parent_children[parent_id]
|
||||
|
||||
for index, child_id in enumerate(children_ids):
|
||||
|
||||
@@ -300,7 +185,7 @@ class Treeview:
|
||||
|
||||
self._print_entry(child_id, prefix, connector_symbol, partnumber_background_color)
|
||||
|
||||
if child_id in self._parent_children:
|
||||
if child_id in parent_children:
|
||||
next_prefix = self._prefix(prefix, is_last_assembly_child)
|
||||
self._tree_body(parent_id=child_id, prefix=next_prefix)
|
||||
|
||||
@@ -309,7 +194,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_child_ids) * 100)
|
||||
progress = int(amount_migrated_ids / len(self.asm_data.child_ids) * 100)
|
||||
|
||||
column_width = 5
|
||||
border_padding = "".ljust(column_width, "─")
|
||||
@@ -331,14 +216,14 @@ class Treeview:
|
||||
"""Prints tree entry"""
|
||||
|
||||
# Necessary for calculating the padding-width after the partnumber
|
||||
longest_entry = self.max_tree_depth * len(Treeview.prefix_types["PIPE"]) \
|
||||
longest_entry = self.asm_data.max_bom_depth * len(Treeview.prefix_types["PIPE"]) \
|
||||
+ len(connector) \
|
||||
+ len(self.longest_partnumber) \
|
||||
+ 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.id_descriptions[child_id]
|
||||
description = self.asm_data.get_bom_property_values("Bezeichnung")[child_id]
|
||||
|
||||
print(f"{text_before_description} {description}")
|
||||
|
||||
@@ -371,17 +256,19 @@ class Treeview:
|
||||
- Migration completed: green
|
||||
- Migration pending: red"""
|
||||
|
||||
return Treeview.console_colors["GREEN"] if self.migration_status.overview[partnumber] is True else Treeview.console_colors["RED"]
|
||||
return Treeview.console_colors["GREEN"] if self.migration_status.id_status[partnumber] is True else Treeview.console_colors["RED"]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Das Programm gibt Auskunft über den aktuellen Migrationsstand', prog='compare_lists')
|
||||
parser = argparse.ArgumentParser(description='Programm gibt Auskunft über den aktuellen Migrationsstand', prog='Display Migration Status')
|
||||
parser.add_argument('-s', '--sivasid', action='store', required=True, help='Eingabe der SIVAS-Teilenummer, für die der Migrationsstatus angezeigt werden soll', metavar="id")
|
||||
parser.add_argument('-t', '--tree', action='store_true', help='Erzeugt Baumdarstellung aller Teilenummern. grün = Teilenummer vorhanden, rot = Migration ausstehend')
|
||||
parser.add_argument('-e', '--excel', action='store_true', help='Erzeugt Excel-Datei in der alle Teilenummern, die migriert werden müssen, aufgelistet sind')
|
||||
parser.add_argument('-rddir', '--rd_dbase_dir', action='store', default=os.environ.get("CREMIG_DATA"))
|
||||
parser.add_argument('-rdfn', '--rd_dbase_filename', action='store', default=os.environ.get("RD_DATABASE_NAME"))
|
||||
parser.add_argument('-asmdir', '--asm_json_dir', action='store', default=os.environ.get("CREMIG_DATA"))
|
||||
parser.add_argument('-t', '--tree', action='store_true', help='Visualisiert Baugruppenstruktur als Baumstruktur 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('-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')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -389,10 +276,10 @@ if __name__ == '__main__':
|
||||
asm_json_filename = f'{asm_id}.json'
|
||||
|
||||
if args.excel:
|
||||
excel_comparison = ExcelExport(asm_id, args.asm_json_dir, asm_json_filename, args.rd_dbase_dir, args.rd_dbase_filename)
|
||||
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.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)
|
||||
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.generate_tree()
|
||||
else:
|
||||
parser.print_help()
|
||||
Reference in New Issue
Block a user