392 lines
17 KiB
Python
392 lines
17 KiB
Python
import cx_Oracle
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import csv
|
|
import re
|
|
import subprocess
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'jit_oracle_connection'))
|
|
from oracle_connection import OracleConnection
|
|
|
|
args = None
|
|
oracle_connection: OracleConnection = 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.werknorm,
|
|
bez.name1_lieferant(artikel_lieferant.ermittle_hauptlieferant(c.kompteilenr)) hauptlieferant,
|
|
(select min(uebteilenr) from comp where uebteilenr = c.kompteilenr) baugruppe
|
|
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
|
|
|
|
# log_print(f"Stücklistendaten geladen für Baugruppe {uebteilenr}\n")
|
|
|
|
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'])
|
|
artikel_data['Hauptlieferant'] = pos['HAUPTLIEFERANT']
|
|
|
|
header = ['Nummer', 'Position', 'Zeichnungsnummer', 'Bezeichnung', 'Parent', 'Konstruktionstext',
|
|
'Stüli-Text', 'Werkstoff', 'Norm', 'Menge', 'Hauptlieferant']
|
|
|
|
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']),
|
|
'Hauptlieferant': escaped_csv_text(artikel_data['Hauptlieferant'])}
|
|
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)
|
|
|
|
json_data[artikel_data['Zeichnungsnummer']] = artikel_data
|
|
|
|
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:
|
|
cx_Oracle.init_oracle_client(lib_dir=instantclient_path)
|
|
except Exception as err:
|
|
log_print("Error connecting: cx_Oracle.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 = OracleConnection('195.243.223.3', 'linux', instantclient_dir=instantclient_path,
|
|
init_oracle_client=False)
|
|
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', 'Hauptlieferant']
|
|
|
|
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() |