Files
SivasTools/Sivas2Json/Sivas2json.py
T
2024-02-16 09:26:48 +01:00

420 lines
19 KiB
Python

import oracledb
import os
import sys
import json
import csv
import re
import subprocess
import argparse
from datetime import datetime
from jit_oracle_connection import JitOracleConnection
args = None
oracle_connection: JitOracleConnection = None
def striprtf(text):
pattern = re.compile(r"\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^a-z])|([{}])|[\r\n]+|(.)", re.I)
destinations = frozenset((
'aftncn','aftnsep','aftnsepc','annotation','atnauthor','atndate','atnicn','atnid',
'atnparent','atnref','atntime','atrfend','atrfstart','author','background',
'bkmkend','bkmkstart','blipuid','buptim','category','colorschememapping',
'colortbl','comment','company','creatim','datafield','datastore','defchp','defpap',
'do','doccomm','docvar','dptxbxtext','ebcend','ebcstart','factoidname','falt',
'fchars','ffdeftext','ffentrymcr','ffexitmcr','ffformat','ffhelptext','ffl',
'ffname','ffstattext','field','file','filetbl','fldinst','fldrslt','fldtype',
'fname','fontemb','fontfile','fonttbl','footer','footerf','footerl','footerr',
'footnote','formfield','ftncn','ftnsep','ftnsepc','g','generator','gridtbl',
'header','headerf','headerl','headerr','hl','hlfr','hlinkbase','hlloc','hlsrc',
'hsv','htmltag','info','keycode','keywords','latentstyles','lchars','levelnumbers',
'leveltext','lfolevel','linkval','list','listlevel','listname','listoverride',
'listoverridetable','listpicture','liststylename','listtable','listtext',
'lsdlockedexcept','macc','maccPr','mailmerge','maln','malnScr','manager','margPr',
'mbar','mbarPr','mbaseJc','mbegChr','mborderBox','mborderBoxPr','mbox','mboxPr',
'mchr','mcount','mctrlPr','md','mdeg','mdegHide','mden','mdiff','mdPr','me',
'mendChr','meqArr','meqArrPr','mf','mfName','mfPr','mfunc','mfuncPr','mgroupChr',
'mgroupChrPr','mgrow','mhideBot','mhideLeft','mhideRight','mhideTop','mhtmltag',
'mlim','mlimloc','mlimlow','mlimlowPr','mlimupp','mlimuppPr','mm','mmaddfieldname',
'mmath','mmathPict','mmathPr','mmaxdist','mmc','mmcJc','mmconnectstr',
'mmconnectstrdata','mmcPr','mmcs','mmdatasource','mmheadersource','mmmailsubject',
'mmodso','mmodsofilter','mmodsofldmpdata','mmodsomappedname','mmodsoname',
'mmodsorecipdata','mmodsosort','mmodsosrc','mmodsotable','mmodsoudl',
'mmodsoudldata','mmodsouniquetag','mmPr','mmquery','mmr','mnary','mnaryPr',
'mnoBreak','mnum','mobjDist','moMath','moMathPara','moMathParaPr','mopEmu',
'mphant','mphantPr','mplcHide','mpos','mr','mrad','mradPr','mrPr','msepChr',
'mshow','mshp','msPre','msPrePr','msSub','msSubPr','msSubSup','msSubSupPr','msSup',
'msSupPr','mstrikeBLTR','mstrikeH','mstrikeTLBR','mstrikeV','msub','msubHide',
'msup','msupHide','mtransp','mtype','mvertJc','mvfmf','mvfml','mvtof','mvtol',
'mzeroAsc','mzeroDesc','mzeroWid','nesttableprops','nextfile','nonesttables',
'objalias','objclass','objdata','object','objname','objsect','objtime','oldcprops',
'oldpprops','oldsprops','oldtprops','oleclsid','operator','panose','password',
'passwordhash','pgp','pgptbl','picprop','pict','pn','pnseclvl','pntext','pntxta',
'pntxtb','printim','private','propname','protend','protstart','protusertbl','pxe',
'result','revtbl','revtim','rsidtbl','rxe','shp','shpgrp','shpinst',
'shppict','shprslt','shptxt','sn','sp','staticval','stylesheet','subject','sv',
'svb','tc','template','themedata','title','txe','ud','upr','userprops',
'wgrffmtfilter','windowcaption','writereservation','writereservhash','xe','xform',
'xmlattrname','xmlattrvalue','xmlclose','xmlname','xmlnstbl',
'xmlopen',
))
# Translation of some special characters.
specialchars = {
'par': '\n',
'sect': '\n\n',
'page': '\n\n',
'line': '\n',
'tab': '\t',
'emdash': '\u2014',
'endash': '\u2013',
'emspace': '\u2003',
'enspace': '\u2002',
'qmspace': '\u2005',
'bullet': '\u2022',
'lquote': '\u2018',
'rquote': '\u2019',
'ldblquote': '\201C',
'rdblquote': '\u201D',
}
stack = []
ignorable = False # Whether this group (and all inside it) are "ignorable".
ucskip = 1 # Number of ASCII characters to skip after a unicode character.
curskip = 0 # Number of ASCII characters left to skip
out = [] # Output buffer.
for match in pattern.finditer(text):
word,arg,hex,char,brace,tchar = match.groups()
if brace:
curskip = 0
if brace == '{':
# Push state
stack.append((ucskip,ignorable))
elif brace == '}':
# Pop state
ucskip,ignorable = stack.pop()
elif char: # \x (not a letter)
curskip = 0
if char == '~':
if not ignorable:
out.append('\xA0')
elif char in '{}\\':
if not ignorable:
out.append(char)
elif char == '*':
ignorable = True
elif word: # \foo
curskip = 0
if word in destinations:
ignorable = True
elif ignorable:
pass
elif word in specialchars:
out.append(specialchars[word])
elif word == 'uc':
ucskip = int(arg)
elif word == 'u':
c = int(arg)
if c < 0: c += 0x10000
if c > 127: out.append(chr(c)) #NOQA
else: out.append(chr(c))
curskip = ucskip
elif hex: # \'xx
if curskip > 0:
curskip -= 1
elif not ignorable:
c = int(hex,16)
if c > 127: out.append(chr(c)) #NOQA
else: out.append(chr(c))
elif tchar:
if curskip > 0:
curskip -= 1
elif not ignorable:
out.append(tchar)
return ''.join(out)
def log_print(text):
global args
if args.para_silent:
print(text)
def log_input(text):
global args
if args.para_silent:
print(text)
def escaped_csv_text(text):
if not text:
return ''
return str(text).replace(';', '\\;')
def artikel_daten_schreiben(uebteilenr: str, json_data: {}, csv_rows: [{}], level: int):
global oracle_connection
sql = f"""select lpad(c.posnr || '/' || c.varianten, length(c.posnr || '/' || c.varianten) + :ebene, '.') position,
c.kompteilenr zeichnungsnummer,
p.bez bezeichnung,
(select sivas_aj.jit_tools.nclob_to_varchar2(g.text_nclob)
from grnr_text g
where g.grnr_teilenr = c.kompteilenr
and g.art = 'T'
and g.lfd_nr = 1
and g.sprache = 'D') konstruktionstext,
decode(c.abw_anzahl, null, null, c.abw_anzahl || ' Stck á ' || c.laenge || ' mm') stueli_text,
c.werkstoff,
c.menge,
c.abw_anzahl,
c.laenge,
c.werknorm,
bez.name1_lieferant(artikel_lieferant.ermittle_hauptlieferant(c.kompteilenr)) hauptlieferant,
(select min(uebteilenr) from comp where uebteilenr = c.kompteilenr) baugruppe,
decode(nvl(c.dispo_aktiv_jn, 'N'), 'J', 'True', 'False') dispo_aktiv_jn,
decode(c.ruestcode, null, null, c.ruestcode || ' - ' || bez.bez_ruestcode(c.ruestcode)) ruestcode,
decode(nvl(c.kz_lieferanten_beistellung, 'N'), 'J', 'True', 'False') kz_lieferanten_beistellung
from comp c,
parts p
where p.teilenr = c.kompteilenr and uebteilenr = :uebteilenr
order by c.posnr, c.varianten"""
cursor = oracle_connection.cursor()
cursor.execute(sql, [level, uebteilenr])
columns = [col[0] for col in cursor.description]
cursor.rowfactory = lambda *args: dict(zip(columns, args))
pos_data = cursor.fetchall()
if not pos_data:
log_print(f"Keine Datensätze für diese Parameter gefunden")
return
for pos in pos_data:
artikel_data = {}
artikel_data['Position'] = pos['POSITION']
artikel_data['Zeichnungsnummer'] = pos['ZEICHNUNGSNUMMER']
artikel_data['Parent'] = uebteilenr
artikel_data['Bezeichnung'] = pos['BEZEICHNUNG']
if not pos['KONSTRUKTIONSTEXT']:
artikel_data['Konstruktionstext'] = str()
else:
artikel_data['Konstruktionstext'] = striprtf(str(pos['KONSTRUKTIONSTEXT']))
artikel_data['Stüli-Text'] = pos['STUELI_TEXT']
artikel_data['Werkstoff'] = pos['WERKSTOFF']
artikel_data['Norm'] = pos['WERKNORM']
artikel_data['Menge'] = int(pos['MENGE'])
if not pos['ABW_ANZAHL']:
artikel_data['Abweichende Anzahl'] = str()
else:
artikel_data['Abweichende Anzahl'] = int(pos['ABW_ANZAHL'])
if not pos['LAENGE']:
artikel_data['Länge'] = str()
else:
artikel_data['Länge'] = int(pos['LAENGE'])
artikel_data['Hauptlieferant'] = pos['HAUPTLIEFERANT']
artikel_data['Dispoaktiv'] = pos['DISPO_AKTIV_JN']
artikel_data['Rüstcode'] = pos['RUESTCODE']
artikel_data['Lieferantenbeistellung'] = pos['KZ_LIEFERANTEN_BEISTELLUNG']
header = ['Nummer', 'Position', 'Zeichnungsnummer', 'Bezeichnung', 'Parent', 'Konstruktionstext',
'Stüli-Text', 'Werkstoff', 'Norm', 'Menge', 'Abweichende Anzahl', 'Länge', 'Hauptlieferant', 'Dispoaktiv', 'Rüstcode', 'Lieferantenbeistellung']
csv_row = {'Nummer': escaped_csv_text(artikel_data['Zeichnungsnummer']),
'Position': escaped_csv_text(artikel_data['Position']),
'Zeichnungsnummer': escaped_csv_text(artikel_data['Zeichnungsnummer']),
'Bezeichnung': escaped_csv_text(artikel_data['Bezeichnung']),
'Parent': escaped_csv_text(artikel_data['Parent']),
'Konstruktionstext': escaped_csv_text(artikel_data['Konstruktionstext']),
'Stüli-Text': escaped_csv_text(artikel_data['Stüli-Text']),
'Werkstoff': escaped_csv_text(artikel_data['Werkstoff']),
'Norm': escaped_csv_text(artikel_data['Norm']),
'Menge': escaped_csv_text(artikel_data['Menge']),
'Abweichende Anzahl': escaped_csv_text(artikel_data['Abweichende Anzahl']),
'Länge': escaped_csv_text(artikel_data['Länge']),
'Hauptlieferant': escaped_csv_text(artikel_data['Hauptlieferant']),
'Dispoaktiv': escaped_csv_text(artikel_data['Dispoaktiv']),
'Rüstcode': escaped_csv_text(artikel_data['Rüstcode']),
'Lieferantenbeistellung': escaped_csv_text(artikel_data['Lieferantenbeistellung'])}
csv_rows.append(csv_row)
sql = f"""select o.agnr,
o.kostenst kostenstelle,
o.verricht_text,
decode(o.codeausw,
'J',
(select name1 from gepa where kdnr = o.kostenst),
(select c.bez from centre c where c.kostenst = o.kostenst)) bezeichnung,
nvl(o.ruestzeit * 60, 0) ruestzeit,
nvl(o.stueckzeit * 60, 0) stueckzeit
from opplan o
where o.teilenr = :teilenr"""
cursor.execute(sql, [pos['ZEICHNUNGSNUMMER']])
columns = [col[0] for col in cursor.description]
cursor.rowfactory = lambda *args: dict(zip(columns, args))
ag_data = cursor.fetchall()
first_time = True
for ag in ag_data:
if first_time:
artikel_data['Arbeitsgänge'] = []
first_time = False
ag_content = {}
ag_content['Nummer'] = int(ag['AGNR'])
ag_content['Kostenstelle'] = int(ag['KOSTENSTELLE'])
ag_content['Bezeichnung'] = ag['BEZEICHNUNG']
ag_content['Verrichttext'] = ag['VERRICHT_TEXT']
ag_content['Rüstzeit'] = int(ag['RUESTZEIT'])
ag_content['Stückzeit'] = int(ag['STUECKZEIT'])
artikel_data['Arbeitsgänge'].append(ag_content)
artikel_data_list_of_dict = [artikel_data]
if artikel_data['Zeichnungsnummer'] in json_data:
artikel_data_list_of_dict = json_data[artikel_data['Zeichnungsnummer']]
artikel_data_list_of_dict.append(artikel_data)
json_data[artikel_data['Zeichnungsnummer']] = artikel_data_list_of_dict
if pos['BAUGRUPPE']:
artikel_daten_schreiben(pos['BAUGRUPPE'], json_data, csv_rows, level+1)
def main():
global args
global oracle_connection
instantclient_path = r"\\195.243.223.3\sivas_schreiben\jit\oracle\bin\x64"
os.environ["PATH"] = instantclient_path + ";" + os.environ["PATH"]
parser = argparse.ArgumentParser(description='Exportiert Stücklisten und deren Arbeitsgänge in eine JSON-, bzw. '
'CSV-Datei.')
parser.add_argument('-o', '--output', dest='para_output_dir',
default=os.path.join(os.path.join(os.environ['USERPROFILE']), 'Desktop'),
help='Spezifiziert das Zielverzeichnis, wo die Export-Datei abgelegt wird.')
parser.add_argument('--format', dest='para_format', default='json', help='Spezifiziert den Typ der Export-Datei.')
parser.add_argument('--silent', dest='para_silent', action='store_false',
help='Deaktiviert die Ausgabe und Benutzereingaben.')
parser.add_argument('--openresult', dest='para_openresult', action='store_true',
help='Spezifiziert, ob nach dem Export die Export-Datei geöffnet werden soll.')
parser.add_argument('-number', dest='para_number', help='Einzelne Artikelnummer')
parser.add_argument('-numbers', dest='para_numbers', help='Kommaseparierte liste von Artikelnummern')
parser.add_argument('-from', dest='para_from', help="Beginn des Artikelnummernbereichs, bspw.: 4700")
parser.add_argument('-to', dest='para_to', help="Ende des Artikelnummernbereichs, bspw.: 4999")
parser.add_argument('-export_pattern', dest='para_export_pattern', help="Angabe eines Artikelnummernbereichs mit "
"Wildcards, bspw.: 87X123X45")
args = parser.parse_args()
try:
oracledb.init_oracle_client(lib_dir=instantclient_path)
except Exception as err:
log_print("Error connecting: oracledb.init_oracle_client()")
log_print(err)
sys.exit(1)
log_print('=== Start Sivas2json ===\n')
log_print('Prüfe Parameter...')
if not args.para_number and not args.para_numbers and not args.para_from and not args.para_to and not args.para_export_pattern:
log_input("Spezifizieren Sie mindestens die Parameter '-number=4711', '-numbers=4711,4712' "
"oder '-from=47xx' und '-to=49xx'.\nBeliebige Taste drücken um zu beenden...")
return 1
log_print('Parameter OK\n')
log_print('Oracle Verbindung...')
try:
# oracle_connection = JitOracleConnection('195.243.223.3', 'linux', instantclient_dir=instantclient_path,
# init_oracle_client=False)
oracle_connection = JitOracleConnection(db_host='195.243.223.3', db_sid='linux', db_user='sivas', db_pwd='forte',
db_database='sivas', instantclient_dir=instantclient_path, tns_admin=None, app=None,
init_oracle_client=True)
except Exception as e:
log_print(str(e))
log_print('Oracle Verbindung OK\n')
sql = """select distinct uebteilenr from comp where ("""
or_needed = False
if args.para_number:
sql += f" uebteilenr = '{args.para_number}'"
or_needed = True
if args.para_numbers:
args.para_numbers = ','.join(["'" + x.strip() + "'" for x in args.para_numbers.split(',')])
if or_needed:
sql += ' or '
sql += f" uebteilenr in ({args.para_numbers})"
or_needed = True
if args.para_from and args.para_to:
if or_needed:
sql += ' or '
sql += f" uebteilenr between '{args.para_from}' and '{args.para_to}'"
if args.para_export_pattern:
if or_needed:
sql += ' or '
sql += f" uebteilenr like '{args.para_export_pattern.replace('X', '_').replace('x', '_')}'"
sql += """) """
cursor = oracle_connection.cursor()
cursor.execute(sql)
columns = [col[0] for col in cursor.description]
cursor.rowfactory = lambda *args: dict(zip(columns, args))
baugruppen = cursor.fetchall()
if not baugruppen:
if oracle_connection.connection():
oracle_connection.connection().close()
log_input(f"Keine Datensätze für diese Parameter gefunden")
return 1
file_count = 0
export_filename = str()
header = ['Nummer', 'Position', 'Zeichnungsnummer', 'Bezeichnung', 'Parent', 'Konstruktionstext',
'Stüli-Text', 'Werkstoff', 'Norm', 'Menge', 'Abweichende Anzahl', 'Länge', 'Hauptlieferant',
'Dispoaktiv', 'Rüstcode', 'Lieferantenbeistellung']
for baugruppe in baugruppen:
json_data = {}
csv_rows = [{}]
artikel_daten_schreiben(uebteilenr=baugruppe['UEBTEILENR'], json_data=json_data, csv_rows=csv_rows, level=0)
export_filename = os.path.join(args.para_output_dir, baugruppe['UEBTEILENR'] + '.' + args.para_format.lower())
log_print(f"Schreibe Export-Datei {export_filename} ...")
# Writing json
with open(export_filename, "w+", encoding='utf8', newline='') as outfile:
if args.para_format.lower() == 'csv':
writer = csv.DictWriter(outfile, delimiter=";", fieldnames=header, escapechar='\\')
# writer.writerow(header)
writer.writeheader()
for csv_row in csv_rows:
writer.writerow(csv_row)
else:
outfile.write(json.dumps(json_data, indent=4, ensure_ascii=False))
if args.para_format.lower() == 'csv':
lines = []
with open(export_filename, 'r') as f:
lines = f.readlines()
with open(export_filename, 'w') as f:
f.writelines(lines[:1] + lines[2:])
log_print(f"Export-Datei geschrieben nach: {export_filename}.")
file_count += 1
if oracle_connection.connection():
oracle_connection.connection().close()
if args.para_openresult:
if file_count > 1:
log_input("Beliebige Taste drücken um Programm zu beenden und Ordner zu öffnen...")
subprocess.Popen(['explorer', args.para_output_dir])
elif file_count == 1:
log_input("Beliebige Taste drücken um Programm zu beenden und Datei zu öffnen...")
subprocess.Popen([export_filename], shell=True)
return 0
if __name__ == '__main__':
main()