Basierend auf Commit d905896 können jetzt im Treeview Nummern aus der Stücklistenstruktur sowie aus der Zusammenfassung ausgeschlossen werden. Nummern, die explizit oder implizit ausgeschlossen wurden (implizit, wenn sie Bestandteil einer Baugruppe sind, die ausgeschlossen wurde), dann werden diese Nummern in der Zusammenfassung aufgelistet, um dem User zu verdeutlichen, dass die Darstellung des Baumes, bzw. der Zusammenfassung von der tatsächlichen Struktur abweicht. Zur Angabe der ausgeschlossenen Nummern aus dem Baum können die neuen Schalter '--rm_bom_ids_str' und '--rm_bom_ids_cfg' genutzt werden. Bei ersterem können die Nummern als Argumente nacheinander mit Leerzeichen getrennt angegeben werden. Bei zweiterem Fall wird eine Konfigurationsdatei mit darin definiertem Sektionsnamen angegeben. Analog dazu gibt es zum Ausschluss der Teilenummern aus den Anzahlen der Zusammenfassung die neuen Schalter '--rm_summary_ids_str' und '--rm_summary_ids_cfg'. Allgemein wurde auch die Angabe der Argumente konkretisiert und z.B. durch CustomActions zur Validierung der Argumente verbessert.
This commit is contained in:
+147
-70
@@ -1,7 +1,8 @@
|
|||||||
from manage_configs import SivasBOM, RuleDesignerDatabaseConfig, MigrationStatus, SivasTeilestamm
|
from manage_configs import SivasBOM, RuleDesignerDatabaseConfig, MigrationStatus, SivasTeilestamm, BOMConfig
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import openpyxl
|
import openpyxl
|
||||||
|
from itertools import zip_longest
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Dieses Programm kann:
|
Dieses Programm kann:
|
||||||
@@ -10,6 +11,23 @@ Dieses Programm kann:
|
|||||||
- die oben Darstellung in der Excel-Datei als Baum in der Konsole ausgeben
|
- die oben Darstellung in der Excel-Datei als Baum in der Konsole ausgeben
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class GetExcludedIDsFromCFG(argparse.Action):
|
||||||
|
def __call__(self, parser, namespace, values, option_string = None):
|
||||||
|
bom_config = BOMConfig(os.path.dirname(values[0]), os.path.basename(values[0]))
|
||||||
|
excluded_ids = bom_config.get_excluded_ids(values[1])
|
||||||
|
setattr(namespace, self.dest, excluded_ids)
|
||||||
|
|
||||||
|
class LStrip(argparse.Action):
|
||||||
|
def __call__(self, parser, namespace, values, option_string = None):
|
||||||
|
setattr(namespace, self.dest, values.lstrip())
|
||||||
|
|
||||||
|
class LocateSQLCredentials(argparse.Action):
|
||||||
|
def __call__(self, parser, namespace, values, option_string = None):
|
||||||
|
if not os.path.isfile(os.environ.get("RD_SQL_DATABASE_CREDENTIALS")):
|
||||||
|
print(f"ACHTUNG: Blau hinterlegte Nummern (Teilenummern die ins PDM migriert wurden, aber aktuell nicht freigegeben sind) werden nicht dargestellt, da nicht auf die Login-Daten für die RD-SQL-Datenbank zugegriffen werden kann. Bitte prüfen, ob die Login-Datei '{os.environ.get("RD_SQL_DATABASE_CREDENTIALS")}' existiert. Auch prüfen, ob die Netzlaufwerke verbunden sind!")
|
||||||
|
setattr(namespace, self.dest, True)
|
||||||
|
|
||||||
|
|
||||||
class ExcelExport:
|
class ExcelExport:
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
@@ -168,21 +186,26 @@ class Treeview:
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
asm_id,
|
asm_id:str,
|
||||||
asm_json_dir,
|
asm_json_dir:str,
|
||||||
asm_json_filename,
|
asm_json_filename:str,
|
||||||
asm_csv_dir,
|
asm_csv_dir:str,
|
||||||
asm_csv_filename,
|
asm_csv_filename:str,
|
||||||
rd_dbase_dir,
|
rd_dbase_dir:str,
|
||||||
rd_dbase_filename,
|
rd_dbase_filename:bool=False,
|
||||||
load_local_asm_json,
|
load_local_asm_json:bool=False,
|
||||||
load_local_asm_csv,
|
load_local_asm_csv:bool=False,
|
||||||
load_local_rd_dbase,
|
load_local_rd_dbase:bool=False,
|
||||||
no_blue_ids):
|
no_blue_ids:bool=False,
|
||||||
|
excluded_bom_ids:list=None,
|
||||||
|
excluded_summary_ids:list=None):
|
||||||
|
|
||||||
self.asm_id = asm_id
|
self.asm_id = asm_id
|
||||||
self.asm_bom = SivasBOM(asm_json_dir, asm_json_filename, load_local_asm_json)
|
self.asm_bom = SivasBOM(asm_id, asm_json_dir, asm_json_filename, load_local_asm_json, excluded_bom_ids)
|
||||||
self.asm_teilestamm = SivasTeilestamm(asm_csv_dir, asm_csv_filename, load_local_asm_csv)
|
self.asm_teilestamm = SivasTeilestamm(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.all_excluded_summary_ids = excluded_summary_ids
|
||||||
|
self._excluded_summary_ids_in_bom = None
|
||||||
self.no_blue_ids = no_blue_ids
|
self.no_blue_ids = no_blue_ids
|
||||||
self._migration_status = None
|
self._migration_status = None
|
||||||
self._longest_entry = None
|
self._longest_entry = None
|
||||||
@@ -250,17 +273,39 @@ class Treeview:
|
|||||||
next_prefix = self._set_next_prefix(prefix, id, last_child)
|
next_prefix = self._set_next_prefix(prefix, id, last_child)
|
||||||
self._print_tree(parent_id=id, prefix=next_prefix)
|
self._print_tree(parent_id=id, prefix=next_prefix)
|
||||||
|
|
||||||
|
def _remove_ids_from_summary_amount(self, ids: list) -> list:
|
||||||
|
"""
|
||||||
|
Reduziert die Anzahl in der Zusammenfassung (z.B. der nicht migrierten Teilenummern),
|
||||||
|
um die Anzahl an Teilenummern, die vom User aus der Zusammenfassung ausgeschlossen wurden
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.all_excluded_summary_ids is None:
|
||||||
|
return ids
|
||||||
|
return list(filter(lambda id: id not in self.excluded_summary_ids_in_bom, ids))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def excluded_summary_ids_in_bom(self) -> list:
|
||||||
|
""" Gibt alle Teilenummern zurück, die im Baum vorliegen, aber nicht in der Anzahl der Zusammenfassung enthalten sein sollen"""
|
||||||
|
|
||||||
|
if self._excluded_summary_ids_in_bom is None:
|
||||||
|
self._excluded_summary_ids_in_bom = list(filter(lambda id: id in self.asm_bom.bom_data, self.all_excluded_summary_ids))
|
||||||
|
return self._excluded_summary_ids_in_bom
|
||||||
|
|
||||||
def _print_summary(self):
|
def _print_summary(self):
|
||||||
|
|
||||||
amount_in_rd = len(self.migration_status.ids_in_rd)
|
ids_in_rd = self.migration_status.ids_in_rd
|
||||||
amount_pending = len(self.migration_status.ids_pending)
|
ids_pending = self.migration_status.ids_pending
|
||||||
|
|
||||||
|
amount_in_rd = len(self._remove_ids_from_summary_amount(ids_in_rd))
|
||||||
|
amount_pending = len(self._remove_ids_from_summary_amount(ids_pending))
|
||||||
|
|
||||||
if not self.no_blue_ids:
|
if not self.no_blue_ids:
|
||||||
amount_in_work = len(self.migration_status.ids_in_progress)
|
ids_in_progress = self.migration_status.ids_in_progress
|
||||||
|
amount_in_work = len(self._remove_ids_from_summary_amount(ids_in_progress))
|
||||||
amount_released = amount_in_rd - amount_in_work
|
amount_released = amount_in_rd - amount_in_work
|
||||||
|
|
||||||
progress = int(amount_in_rd / len(self.asm_bom.child_ids) * 100)
|
child_ids = self.asm_bom.child_ids
|
||||||
|
progress = int(amount_in_rd / len(self._remove_ids_from_summary_amount(child_ids)) * 100)
|
||||||
|
|
||||||
column_width = 5
|
column_width = 5
|
||||||
border_padding = "".ljust(column_width, "─")
|
border_padding = "".ljust(column_width, "─")
|
||||||
@@ -285,14 +330,35 @@ class Treeview:
|
|||||||
│ Auswertung {empty_padding}│
|
│ Auswertung {empty_padding}│
|
||||||
├─┬───────────────────────────────────────┬─{border_padding}┤
|
├─┬───────────────────────────────────────┬─{border_padding}┤
|
||||||
"""
|
"""
|
||||||
|
progress_segment = f"""
|
||||||
progress = f"""
|
|
||||||
├─┴───────────────────────────────────────┼─{border_padding}┤
|
├─┴───────────────────────────────────────┼─{border_padding}┤
|
||||||
│ Migrationsfortschritt │ {f"{progress}%".ljust(column_width)}│
|
│ Migrationsfortschritt │ {f"{progress}%".ljust(column_width)}│
|
||||||
╰─────────────────────────────────────────┴─{border_padding}╯
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
print(header + green_entry + blue_entry + red_entry + progress)
|
bottom_row = f"╰─────────────────────────────────────────┴─{border_padding}╯"
|
||||||
|
|
||||||
|
|
||||||
|
exclusion_description = ""
|
||||||
|
excluded_id_list = ""
|
||||||
|
|
||||||
|
if self.asm_bom.excluded_ids_in_bom or self.excluded_summary_ids_in_bom:
|
||||||
|
|
||||||
|
exclusion_description = f"""├─────────────────────────────────────────┴─{border_padding}┤
|
||||||
|
│ {empty_padding}│
|
||||||
|
├───────────────────────────────────────────{border_padding}┤
|
||||||
|
│ Ausgeschlossene Teilenummern {empty_padding}│
|
||||||
|
│──────────────────────┬────────────────────{border_padding}┤
|
||||||
|
│ Baum │ Auswertung {empty_padding}│
|
||||||
|
├──────────────────────┼────────────────────{border_padding}┤
|
||||||
|
"""
|
||||||
|
|
||||||
|
for bom_id, smry_id in zip_longest(self.asm_bom.excluded_ids_in_bom, self.excluded_summary_ids_in_bom, fillvalue=9*" "):
|
||||||
|
excluded_id_list += f"│ { bom_id} │ {smry_id} {empty_padding}│\n"
|
||||||
|
|
||||||
|
bottom_row = f"╰──────────────────────┴────────────────────{border_padding}╯"
|
||||||
|
|
||||||
|
|
||||||
|
print(header + green_entry + blue_entry + red_entry + progress_segment + exclusion_description + excluded_id_list + bottom_row)
|
||||||
|
|
||||||
def _print_entry(self, child_id, prefix, connector, color):
|
def _print_entry(self, child_id, prefix, connector, color):
|
||||||
|
|
||||||
@@ -352,58 +418,69 @@ class Treeview:
|
|||||||
|
|
||||||
return Treeview.console_colors["GREEN"]
|
return Treeview.console_colors["GREEN"]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
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='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('-rd_ids_filedir', 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('-rd_ids_filename', 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('-rd_ids_local', 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('-asm_json_filedir', 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('-asm_json_local', 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('-asm_csv_filedir', 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('-asm_csv_local', 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('-no_blue_ids', action='store_true', default=False, help='Legt fest, ob blaue IDs im Baum angezeigt werden sollen. Erforderlich, wenn keine Verbindung zu RDServer besteht, da SQL-Abfrage durchgeführt werden muss')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
def main():
|
||||||
|
|
||||||
asm_id = args.sivasid.lstrip()
|
parser = argparse.ArgumentParser(description='Programm gibt Auskunft über den aktuellen Migrationsstand einer SIVAS-Teilenummer',
|
||||||
asm_bom_filename = f'{asm_id}.json'
|
prog='compare_lists',
|
||||||
asm_metadata_filename = f'{asm_id}.csv'
|
formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=50)) # Hilfetext auch bei längeren Argumenten in gleicher Zeile wie Argument darstellen
|
||||||
|
|
||||||
if not args.no_blue_ids:
|
# Custom Actions
|
||||||
if not os.path.isfile(os.environ.get("RD_SQL_DATABASE_CREDENTIALS")):
|
parser.register("action", "GetIDsFromCFG", GetExcludedIDsFromCFG)
|
||||||
print(f"ACHTUNG: Blau hinterlegte Nummern (Teilenummern die ins PDM migriert wurden, aber aktuell nicht freigegeben sind) werden nicht dargestellt, da nicht auf die Login-Daten für die RD-SQL-Datenbank zugegriffen werden kann. Bitte prüfen, ob die Login-Datei '{os.environ.get("RD_SQL_DATABASE_CREDENTIALS")}' existiert. Auch prüfen, ob die Netzlaufwerke verbunden sind!")
|
parser.register("action", "lstrip", LStrip)
|
||||||
args.no_blue_ids = True
|
parser.register("action", "LocateSQLCredentials", LocateSQLCredentials)
|
||||||
|
|
||||||
if args.excel:
|
parser.add_argument('sivas_id', action='lstrip', help='Teilenummer, für die Migrationsüberblick generiert werden soll.', metavar="SIVAS-Teilenummer")
|
||||||
excel_comparison = ExcelExport(asm_id=asm_id,
|
parser.add_argument('output_type', choices=["tree", "excel"], help="'tree' = Darstellung des Überblicks als Baum in der Konsole. 'excel' = Export des Überblicks als Excel")
|
||||||
asm_json_dir=args.asm_json_filedir,
|
parser.add_argument('--rd_ids_filepath', action='store', default=os.path.join(os.environ.get("CREMIG_DATA"), "RD_Teilenummern.csv"), help='Verzeichnis der CSV-Datei mit den RuleDesigner-Nummern. Default: $CREMIG_DATA', metavar="filepath")
|
||||||
asm_json_filename=asm_bom_filename,
|
parser.add_argument('--local_rd_ids', action='store_true', default=False, help='Lokales laden der CSV mit den RuleDesigner-Nummern (RD-Teilenummer.csv) statt Kopieren von Server')
|
||||||
asm_csv_dir=args.rd_ids_filedir,
|
parser.add_argument('--bom_filedir', action='store', default=os.environ.get("CREMIG_DATA"),help='Verzeichnis der JSON-Datei mit den Stücklisteninformationen. Default: $CREMIG_DATA', metavar='dir')
|
||||||
asm_csv_filename=asm_metadata_filename,
|
parser.add_argument('--local_bom', action='store_true', default=False, help='Lokales laden der JSON mit den Stücklisteninformationen statt Generierung')
|
||||||
rd_dbase_dir=args.rd_ids_filedir,
|
parser.add_argument('--teilestamm_filedir', action='store', default=os.environ.get("CREMIG_DATA"), help='Verzeichnis der CSV mit den SIVAS-Teilestamminformationen. Default: $CREMIG_DATA', metavar='dir')
|
||||||
rd_dbase_filename=args.rd_ids_filename,
|
parser.add_argument('--local_teilestamm', action='store_true', default=False, help='Lokales Laden der CSV mit den Teilestamminformationen statt Generierung')
|
||||||
load_local_asm_json=args.asm_json_local,
|
parser.add_argument('--no_blue_ids', action="store_true", default=False, help='Nummern, die in RD vorliegen, aber nicht freigegeben sind, gleich wie freigegeben Nummern einfärben')
|
||||||
load_local_asm_csv=args.asm_csv_local,
|
|
||||||
load_local_rd_dbase=args.asm_csv_local,
|
|
||||||
no_blue_ids=args.no_blue_ids)
|
|
||||||
|
|
||||||
excel_comparison.write_excel()
|
exclude_from_overview = parser.add_mutually_exclusive_group()
|
||||||
elif args.tree:
|
exclude_from_overview.add_argument('--rm_bom_ids_str', action="store", nargs="*", default=None, dest="excluded_bom_ids", help="Angabe aller Nummern, die vom Migrationsüberblick ausgeschlossen sein sollen", metavar="id1 id2")
|
||||||
treeview_comparison = Treeview(asm_id=asm_id,
|
exclude_from_overview.add_argument('--rm_bom_ids_cfg', action="GetIDsFromCFG", nargs=2, default=None, dest="excluded_bom_ids", help="cfg-Pfad & Sektion, in der Nummern aufgelistet sind, die nicht im Überblick dargestellt sein sollen", metavar=("path", "section"))
|
||||||
asm_json_dir=args.asm_json_filedir,
|
|
||||||
asm_json_filename=asm_bom_filename,
|
exclude_from_summary = parser.add_mutually_exclusive_group()
|
||||||
asm_csv_dir=args.rd_ids_filedir,
|
exclude_from_summary.add_argument('--rm_summary_ids_str', action="store", nargs="*", default=None, dest="excluded_summary_ids", help="Angabe aller Nummern, die von der Zusammenfassung ausgeschlossen sein sollen", metavar="id1 id2")
|
||||||
asm_csv_filename=asm_metadata_filename,
|
exclude_from_summary.add_argument('--rm_summary_ids_cfg', action="GetIDsFromCFG", nargs=2, default=None, dest="excluded_summary_ids", help="cfg-Pfad & Sektion, in der Nummern aufgelistet sind, die von der Zusammenfassung ausgeschlossen sein sollen", metavar=("path", "section"))
|
||||||
rd_dbase_dir=args.rd_ids_filedir,
|
|
||||||
rd_dbase_filename=args.rd_ids_filename,
|
args = parser.parse_args()
|
||||||
load_local_asm_json=args.asm_json_local,
|
|
||||||
load_local_asm_csv=args.asm_csv_local,
|
match args.output_type:
|
||||||
load_local_rd_dbase=args.rd_ids_local,
|
case "excel":
|
||||||
no_blue_ids=args.no_blue_ids)
|
excel_comparison = ExcelExport(asm_id=args.sivas_id,
|
||||||
treeview_comparison.generate_tree()
|
asm_json_dir=args.bom_filedir,
|
||||||
else:
|
asm_json_filename=f'{args.sivas_id}.json',
|
||||||
parser.print_help()
|
asm_csv_dir=args.teilestamm_filedir,
|
||||||
|
asm_csv_filename=f"{args.sivas_id}.csv",
|
||||||
|
rd_dbase_dir=os.path.dirname(args.rd_ids_filepath),
|
||||||
|
rd_dbase_filename=os.path.basename(args.rd_ids_filepath),
|
||||||
|
load_local_asm_json=args.local_bom,
|
||||||
|
load_local_asm_csv=args.local_teilestamm,
|
||||||
|
load_local_rd_dbase=args.local_rd_ids,
|
||||||
|
no_blue_ids=args.no_blue_ids)
|
||||||
|
|
||||||
|
excel_comparison.write_excel()
|
||||||
|
case "tree":
|
||||||
|
treeview_comparison = Treeview(asm_id=args.sivas_id,
|
||||||
|
asm_json_dir=args.bom_filedir,
|
||||||
|
asm_json_filename=f'{args.sivas_id}.json',
|
||||||
|
asm_csv_dir=args.teilestamm_filedir,
|
||||||
|
asm_csv_filename=f'{args.sivas_id}.csv',
|
||||||
|
rd_dbase_dir=os.path.dirname(args.rd_ids_filepath),
|
||||||
|
rd_dbase_filename=os.path.basename(args.rd_ids_filepath),
|
||||||
|
load_local_asm_json=args.local_bom,
|
||||||
|
load_local_asm_csv=args.local_teilestamm,
|
||||||
|
load_local_rd_dbase=args.local_rd_ids,
|
||||||
|
no_blue_ids=args.no_blue_ids,
|
||||||
|
excluded_bom_ids=args.excluded_bom_ids,
|
||||||
|
excluded_summary_ids=args.excluded_summary_ids)
|
||||||
|
|
||||||
|
treeview_comparison.generate_tree()
|
||||||
|
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user