Compare commits

1 Commits

Author SHA1 Message Date
9eb63b111d Dateien nach "/" hochladen 2026-04-01 08:00:29 +02:00

View File

@@ -1,256 +1,256 @@
""" """
repovizcheck.py repovizcheck.py
Kurzbeschreibung: Kurzbeschreibung:
Dieses Skript vergleicht RepoViz-Metadaten (Annotationen in Kornshell-Skripten: "#@modul:", "#@quelle:", "#@ziel:") Dieses Skript vergleicht RepoViz-Metadaten (Annotationen in Kornshell-Skripten: "#@modul:", "#@quelle:", "#@ziel:")
mit tatsächlich im Skript verwendeten Tabellen (Erkennung über $SCHEMA.<TABELLE> oder ${SCHEMA}<TABELLE>). Es mit tatsächlich im Skript verwendeten Tabellen (Erkennung über $SCHEMA.<TABELLE> oder ${SCHEMA}<TABELLE>). Es
ermöglicht einen schnellen Abgleich, welche Tabellen in RepoViz dokumentiert sind und welche im Code auftauchen. ermöglicht einen schnellen Abgleich, welche Tabellen in RepoViz dokumentiert sind und welche im Code auftauchen.
Aufruf: Aufruf:
python repovizcheck.py <skript.ksh> python repovizcheck.py <skript.ksh>
Wichtige Hinweise: Wichtige Hinweise:
- Akzeptierte Dateiendung: .ksh (case-insensitive). - Akzeptierte Dateiendung: .ksh (case-insensitive).
- Kodierung: versucht zunächst UTF-8, bei Fehlern Fallback auf ISO-8859-1. - Kodierung: versucht zunächst UTF-8, bei Fehlern Fallback auf ISO-8859-1.
- Tabellennamen werden für Vergleiche normalisiert (Uppercase); Regex-Parsing wurde robustifiziert. - Tabellennamen werden für Vergleiche normalisiert (Uppercase); Regex-Parsing wurde robustifiziert.
""" """
from __future__ import print_function from __future__ import print_function
import sys import sys
import re import re
import os.path as p import os.path as p
import argparse import argparse
print(r" ____ __ ___ ____ _ _ ") print(r" ____ __ ___ ____ _ _ ")
print(r"| _ \ ___ _ __ ___ \ \ / (_)____ / ___| |__ ___ ___| | _____ _ __ ") print(r"| _ \ ___ _ __ ___ \ \ / (_)____ / ___| |__ ___ ___| | _____ _ __ ")
print(r"| |_) / _ \ '_ \ / _ \ \ \ / /| |_ / | | | '_ \ / _ \/ __| |/ / _ \ '__|") print(r"| |_) / _ \ '_ \ / _ \ \ \ / /| |_ / | | | '_ \ / _ \/ __| |/ / _ \ '__|")
print(r"| _ < __/ |_) | (_) | \ V / | |/ / | |___| | | | __/ (__| < __/ | ") print(r"| _ < __/ |_) | (_) | \ V / | |/ / | |___| | | | __/ (__| < __/ | ")
print(r"|_| \_\___| .__/ \___/ \_/ |_/___| \____|_| |_|\___|\___|_|\_\___|_| ") print(r"|_| \_\___| .__/ \___/ \_/ |_/___| \____|_| |_|\___|\___|_|\_\___|_| ")
print(r" |_| ") print(r" |_| ")
such_quelle = "#@quelle:" # such_quelle = "#@quelle:" #
such_ziel = "#@ziel:" # such_ziel = "#@ziel:" #
such_modul = "#@modul:" # such_modul = "#@modul:" #
allezeilen = [] # Das Skript wird zeilenwiese in diese Liste geschrieben, eine Zeile gleich ein Element der Liste allezeilen = [] # Das Skript wird zeilenwiese in diese Liste geschrieben, eine Zeile gleich ein Element der Liste
kommentarzeilen = [] # Ein Teil der nicht benoetigten Zeilen kommt in diese Liste kommentarzeilen = [] # Ein Teil der nicht benoetigten Zeilen kommt in diese Liste
codezeilen = [] # Zeilen mit Code kommen in diese Liste codezeilen = [] # Zeilen mit Code kommen in diese Liste
# Liste der gueltigen Dateiendungen (case-insensitive) # Liste der gueltigen Dateiendungen (case-insensitive)
perm_file_suffix = ['.ksh'] # Liste der gueltigen Dateiendungen perm_file_suffix = ['.ksh'] # Liste der gueltigen Dateiendungen
# Verwende argparse für robusten CLI-Aufruf und Help # Verwende argparse für robusten CLI-Aufruf und Help
parser = argparse.ArgumentParser(description='Check RepoViz annotations against SQL in a ksh script') parser = argparse.ArgumentParser(description='Check RepoViz annotations against SQL in a ksh script')
parser.add_argument('sourcefile', help='Kornshell script to check (must end with .ksh)') parser.add_argument('sourcefile', help='Kornshell script to check (must end with .ksh)')
args = parser.parse_args() args = parser.parse_args()
sourcefile = args.sourcefile sourcefile = args.sourcefile
# Dateiendung prüfen (case-insensitive) # Dateiendung prüfen (case-insensitive)
file_suffix = p.splitext(sourcefile)[1].lower() file_suffix = p.splitext(sourcefile)[1].lower()
if file_suffix not in perm_file_suffix: if file_suffix not in perm_file_suffix:
print() # Gib eine leere Zeile aus print() # Gib eine leere Zeile aus
print("Only Kornshell scripts can be processed.") # Gib diese Meldung aus print("Only Kornshell scripts can be processed.") # Gib diese Meldung aus
print() print()
sys.exit(1) sys.exit(1)
# Funktion zum Oeffnen einer Datei mit Pruefung # Funktion zum Oeffnen einer Datei mit Pruefung
def get(name): def get(name):
'''Funktion zum Oeffnen einer Datei mit Pruefung (prüft Encoding)''' '''Funktion zum Oeffnen einer Datei mit Pruefung (prüft Encoding)'''
# Versuche zuerst UTF-8, lese einen kleinen Block zur Validierung. # Versuche zuerst UTF-8, lese die gesamte Datei zur Validierung.
try: try:
f = open(name, "r", encoding='utf-8') f = open(name, "r", encoding='utf-8')
except IOError: f.read() # komplette Datei lesen, um Decode-Fehler sicher zu erkennen
print("") f.seek(0)
print("The File", name, "doesn't exist or can't be opened!") return f
print("") except UnicodeDecodeError:
sys.exit(1) # UTF-8 passt nicht — versuche ISO-8859-1
try:
try: f.close()
# Lese ein kleines Stück, um mögliche Decode-Fehler sofort zu erzeugen return open(name, "r", encoding='ISO-8859-1')
f.read(2048) except IOError:
f.seek(0) print("")
return f print("The File", name, "can't be opened with fallback encoding!")
except UnicodeDecodeError: print("")
# UTF-8 scheint nicht zu passen — versuche ISO-8859-1 sys.exit(1)
try: except IOError:
f.close() print("")
return open(name, "r", encoding='ISO-8859-1') print("The File", name, "doesn't exist or can't be opened!")
except IOError: print("")
print("") sys.exit(1)
print("The File", name, "can't be opened with fallback encoding!") except Exception:
print("") # unerwarteter Fehler beim Lesen
sys.exit(1) try:
except Exception: f.close()
# unerwarteter Fehler beim Lesen except Exception:
f.close() pass
print("") print("")
print("Error reading the file", name) print("Error reading the file", name)
print("") print("")
sys.exit(1) sys.exit(1)
return None return None
# Funktion zum Erstellen einer Tabellenliste aus den RepoViz Informationen # Funktion zum Erstellen einer Tabellenliste aus den RepoViz Informationen
def erstelle_liste(datei, typ): # 2 Parameter def erstelle_liste(datei, typ): # 2 Parameter
'''Erstelle eine Liste aus der übergebenen Datei für den Typ der übergeben wurde''' '''Erstelle eine Liste aus der übergebenen Datei für den Typ der übergeben wurde'''
laenge = len(typ) laenge = len(typ)
tabellenliste = [] tabellenliste = []
fobj_in = get(datei) fobj_in = get(datei)
for line in fobj_in: for line in fobj_in:
# entferne nur führende Leerzeichen vor der Prüfung # entferne nur führende Leerzeichen vor der Prüfung
content = line.lstrip() content = line.lstrip()
if content.startswith(typ): if content.startswith(typ):
zeile = content[len(typ):].strip() zeile = content[len(typ):].strip()
# entferne ein mögliches abschließendes Komma # entferne ein mögliches abschließendes Komma
if zeile.endswith(','): if zeile.endswith(','):
zeile = zeile[:-1] zeile = zeile[:-1]
liste = [it.strip() for it in zeile.split(',') if it.strip()] liste = [it.strip() for it in zeile.split(',') if it.strip()]
for item in liste: for item in liste:
# normalisiere auf Großbuchstaben für spätere Vergleiche # normalisiere auf Großbuchstaben für spätere Vergleiche
tabellenliste.append(item.strip().upper()) tabellenliste.append(item.strip().upper())
fobj_in.close() fobj_in.close()
return tabellenliste return tabellenliste
def trennzeile(typ): # Funktion zum Ausgeben einer 80 Zeichen breiten Trennzeile. def trennzeile(typ): # Funktion zum Ausgeben einer 80 Zeichen breiten Trennzeile.
''' '''
Erstellt eine 80 zeichenbreite Zeile mit dem übergebenen Zeichen Erstellt eine 80 zeichenbreite Zeile mit dem übergebenen Zeichen
''' '''
print(typ * 80) # Das Trennzeichen ist variabel und wird der Funktion als Parameter uebergeben. print(typ * 80) # Das Trennzeichen ist variabel und wird der Funktion als Parameter uebergeben.
modulliste = erstelle_liste(sourcefile, such_modul) # Erstelle Liste mit den Modulen modulliste = erstelle_liste(sourcefile, such_modul) # Erstelle Liste mit den Modulen
quelleliste = erstelle_liste(sourcefile, such_quelle) # Erstelle Liste mit den Quellen quelleliste = erstelle_liste(sourcefile, such_quelle) # Erstelle Liste mit den Quellen
zielliste = erstelle_liste(sourcefile, such_ziel) # Erstelle Liste mit den Zielen zielliste = erstelle_liste(sourcefile, such_ziel) # Erstelle Liste mit den Zielen
datei = get(sourcefile) # Oeffnen der Datei mit einer Funktion, die auch prueft ob die Datei existiert datei = get(sourcefile) # Oeffnen der Datei mit einer Funktion, die auch prueft ob die Datei existiert
allezeilen = datei.readlines() # Lesen aller Zeilen in eine Liste allezeilen = datei.readlines() # Lesen aller Zeilen in eine Liste
datei.close() datei.close()
# Versuch die Kommentarzeilen loszuwerden # Versuch die Kommentarzeilen loszuwerden
for zeile in allezeilen: for zeile in allezeilen:
s = zeile.strip() s = zeile.strip()
if not s: if not s:
# leere Zeilen überspringen # leere Zeilen überspringen
continue continue
if s.startswith('#@modul:'): if s.startswith('#@modul:'):
codezeilen.append(s) codezeilen.append(s)
continue continue
# Gruppiere typische Kommentar-/Meta-Zeilen (case-insensitive) # Gruppiere typische Kommentar-/Meta-Zeilen (case-insensitive)
up = s.upper() up = s.upper()
if s.startswith('#') or s.startswith('--') or up.startswith('J000') or up.startswith('SQLFILE') \ if s.startswith('#') or s.startswith('--') or up.startswith('J000') or up.startswith('SQLFILE') \
or up.startswith('FMELDUNG') or up.startswith('ALTER') or up.startswith('F_TRUNCATE') \ or up.startswith('FMELDUNG') or up.startswith('ALTER') or up.startswith('F_TRUNCATE') \
or up == 'FI' or up == 'IF' or up.startswith('F_RUNSTATS'): or up == 'FI' or up == 'IF' or up.startswith('F_RUNSTATS'):
kommentarzeilen.append(s) kommentarzeilen.append(s)
else: else:
codezeilen.append(s) codezeilen.append(s)
trennzeile("+") trennzeile("+")
print("Sorted output of RepoViz information.") print("Sorted output of RepoViz information.")
print("") print("")
ausgabe = "{:10}{:}" # Definition AusgabeFormat 1.Feld begrenzt auf 10 Zeichen, das 2.Feld unbegrenzt ausgabe = "{:10}{:}" # Definition AusgabeFormat 1.Feld begrenzt auf 10 Zeichen, das 2.Feld unbegrenzt
quelleliste.sort() # Sortiere Liste <quelleliste> quelleliste.sort() # Sortiere Liste <quelleliste>
zielliste.sort() # Sortiere Liste <zielliste> zielliste.sort() # Sortiere Liste <zielliste>
print(ausgabe.format(such_modul, modulliste)) # Ausgabe <modulliste> print(ausgabe.format(such_modul, modulliste)) # Ausgabe <modulliste>
print(ausgabe.format(such_quelle, quelleliste)) # Ausgabe <quelleliste> print(ausgabe.format(such_quelle, quelleliste)) # Ausgabe <quelleliste>
print(ausgabe.format(such_ziel, zielliste)) # Ausgabe <zielliste> print(ausgabe.format(such_ziel, zielliste)) # Ausgabe <zielliste>
# Vergleiche sourcefile mit modulliste hier einfuegen # Vergleiche sourcefile mit modulliste hier einfuegen
# Eventuell anpassen, es steht nur der Skriptname in den RepoVizInfos # Eventuell anpassen, es steht nur der Skriptname in den RepoVizInfos
trennzeile("~") trennzeile("~")
trennzeile("") trennzeile("")
print("Comparison of given script name with RepoVizInformation") print("Comparison of given script name with RepoVizInformation")
# Vergleiche nur einmal, normalisiere auf Großbuchstaben # Vergleiche nur einmal, normalisiere auf Großbuchstaben
script_basename = p.basename(sourcefile).upper() script_basename = p.basename(sourcefile).upper()
if script_basename in [m.strip().upper() for m in modulliste]: if script_basename in [m.strip().upper() for m in modulliste]:
print("Script", p.basename(sourcefile), "is in the RepoViz information!") print("Script", p.basename(sourcefile), "is in the RepoViz information!")
else: else:
print("Script", p.basename(sourcefile), "is missing in the RepoViz information!") print("Script", p.basename(sourcefile), "is missing in the RepoViz information!")
# Suche Objekte anhand der RepoVizInformationen # Suche Objekte anhand der RepoVizInformationen
trennzeile("~") trennzeile("~")
print("") print("")
print("Create a list of tables from the existing code: list <allezeilen>") print("Create a list of tables from the existing code: list <allezeilen>")
# Der Code steht schon in der Liste <codezeilen> # Der Code steht schon in der Liste <codezeilen>
neue_liste = [] neue_liste = []
# verbessertes Regex-Parsing: capture-Gruppe für Tabellennamen, compile einmal # verbessertes Regex-Parsing: capture-Gruppe für Tabellennamen, compile einmal
regex = re.compile(r"(?:\$SCHEMA\.|\${SCHEMA}\.)\s*([A-Z0-9_]+)", re.IGNORECASE) regex = re.compile(r"(?:\$SCHEMA\.|\${SCHEMA}\.)\s*([A-Z0-9_]+)", re.IGNORECASE)
# nutze Set für eindeutige Sammlung # nutze Set für eindeutige Sammlung
neue_set = set() neue_set = set()
for line in allezeilen: for line in allezeilen:
for match in regex.findall(line): for match in regex.findall(line):
tabname = match.strip() tabname = match.strip()
if not tabname: if not tabname:
continue continue
# ignoriere Variablen oder temporäre Tabellen # ignoriere Variablen oder temporäre Tabellen
if tabname.startswith('$'): if tabname.startswith('$'):
continue continue
tabname_norm = re.sub(r"[^A-Z0-9_]", "", tabname.upper()) tabname_norm = re.sub(r"[^A-Z0-9_]", "", tabname.upper())
if tabname_norm and not tabname_norm.startswith('TMP_'): if tabname_norm and not tabname_norm.startswith('TMP_'):
neue_set.add(tabname_norm) neue_set.add(tabname_norm)
neue_liste = sorted(neue_set) neue_liste = sorted(neue_set)
print("The list contains:", len(neue_liste), "entries") print("The list contains:", len(neue_liste), "entries")
trennzeile("~") trennzeile("~")
# Vergleich repo -> SQL # Vergleich repo -> SQL
# 2 Teile # 2 Teile
# Quelle - quelleliste # Quelle - quelleliste
# nehme Liste "quelleliste" und suche damit in Liste "neue_liste" # nehme Liste "quelleliste" und suche damit in Liste "neue_liste"
# trennzeile("#") # trennzeile("#")
print("Are the tables of the list", such_quelle, "included in the SQL?") print("Are the tables of the list", such_quelle, "included in the SQL?")
trennzeile("~") trennzeile("~")
for item in quelleliste: for item in quelleliste:
# quelleliste wurde in erstelle_liste bereits normalisiert (upper) # quelleliste wurde in erstelle_liste bereits normalisiert (upper)
if item.upper() in neue_liste: if item.upper() in neue_liste:
ausgabe = "{:12}{:40}{:20}" ausgabe = "{:12}{:40}{:20}"
print(ausgabe.format("The Table", item, "is available")) print(ausgabe.format("The Table", item, "is available"))
else: else:
ausgabe = "{:12}{:40}{:20}" ausgabe = "{:12}{:40}{:20}"
print(ausgabe.format("The Table", item, "is not available")) print(ausgabe.format("The Table", item, "is not available"))
# Quelle - zielliste # Quelle - zielliste
# nehme Liste "zielliste" und suche damit in Liste "neue_liste" # nehme Liste "zielliste" und suche damit in Liste "neue_liste"
trennzeile("~") trennzeile("~")
print("Are the tables of the list", such_ziel, "included in the SQL?") print("Are the tables of the list", such_ziel, "included in the SQL?")
trennzeile("~") trennzeile("~")
for item in zielliste: for item in zielliste:
if item.upper() in neue_liste: if item.upper() in neue_liste:
ausgabe = "{:12}{:40}{:20}" ausgabe = "{:12}{:40}{:20}"
print(ausgabe.format("The Table", item, "is available")) print(ausgabe.format("The Table", item, "is available"))
else: else:
ausgabe = "{:12}{:40}{:20}" ausgabe = "{:12}{:40}{:20}"
print(ausgabe.format("The Table", item, "is not available")) print(ausgabe.format("The Table", item, "is not available"))
trennzeile("~") trennzeile("~")
print("Create unique sorted list from ", such_quelle, "and", such_ziel) print("Create unique sorted list from ", such_quelle, "and", such_ziel)
q_z_liste = sorted({t.upper() for t in (quelleliste + zielliste)}) q_z_liste = sorted({t.upper() for t in (quelleliste + zielliste)})
trennzeile("~") trennzeile("~")
# Vergleich SQL -> repo # Vergleich SQL -> repo
# Quelle - neue_liste # Quelle - neue_liste
# nehme Liste "quelleliste" und suche damit in Liste "neue_liste" # nehme Liste "quelleliste" und suche damit in Liste "neue_liste"
trennzeile("~") trennzeile("~")
print("Are the tables from the SQL included in the RepoViz information?") print("Are the tables from the SQL included in the RepoViz information?")
print("Note: I've merged <#@quelle> and <#@ziel> into one list ") print("Note: I've merged <#@quelle> and <#@ziel> into one list ")
trennzeile("~") trennzeile("~")
for item in neue_liste: for item in neue_liste:
if item in q_z_liste: if item in q_z_liste:
ausgabe = "{:12}{:40}{:20}" ausgabe = "{:12}{:40}{:20}"
print(ausgabe.format("The Table", item, "is available")) print(ausgabe.format("The Table", item, "is available"))
else: else:
ausgabe = "{:12}{:40}{:20}" ausgabe = "{:12}{:40}{:20}"
print(ausgabe.format("The Table", item, "is not available")) print(ausgabe.format("The Table", item, "is not available"))
trennzeile("+") trennzeile("+")
print(r" _____ _ _____ _ ") print(r" _____ _ _____ _ ")
print(r"|_ _| |__ ___ | ____|_ __ __| |") print(r"|_ _| |__ ___ | ____|_ __ __| |")
print(r" | | | '_ \ / _ \ | _| | '_ \ / _` |") print(r" | | | '_ \ / _ \ | _| | '_ \ / _` |")
print(r" | | | | | | __/ | |___| | | | (_| |") print(r" | | | | | | __/ | |___| | | | (_| |")
print(r" |_| |_| |_|\___| |_____|_| |_|\__,_|") print(r" |_| |_| |_|\___| |_____|_| |_|\__,_|")
print("") print("")