
| Current Path : /home/ift/52_procpy/fibu/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : //home/ift/52_procpy/fibu/zz |
# coding: utf8
import os,sys,re,glob,time,sqlite3,hashlib,time,base64,datetime,random
import procpy
import procpy.fibu
addpath = ".."
while len(addpath) < 30:
addpath
sys.path.append(addpath)
addpath = addpath + "/.."
import fibu
#******************************************************************************
class Sync (object):
def __init__ (self):
self.pythoncall = "python3"
try:
self.sortfkt = fibu.sortfkt
except:
self.sortfkt = None
try:
self.synchronized = fibu.synchronized
print("SYNCHRONIZED")
except:
self.synchronized = False
self.kontenrahmen = {}
try:
for kto in fibu.kontenrahmen.split("\n"):
m = re.search(r"^(.*?) +(.*?) *$",kto)
if m:
self.kontenrahmen[m.group(1)] = m.group(2)
except:
pass
#******************************************************************************
def generate_db (self,database_file):
self.dbh = sqlite3.connect(database_file)
self.dbh.row_factory = sqlite3.Row
cursor = self.dbh.cursor()
self.pythoncall = "python3"
cursor.execute("create table if not exists buchungen " +
"(DATUM,BETRAG,KTOA,KTOB,REMARK,ID default '')")
try:
cursor.execute("create index DATUM_IDX on buchungen (DATUM)" )
cursor.execute("create index KTOA_IDX on buchungen (KTOA)" )
cursor.execute("create index KTOB_IDX on buchungen (KTOB)" )
self.database_empty = True
except Exception as e:
self.database_empty = False
#******************************************************************************
def mark (self,remark=""):
t = time.clock()
if 't0' in vars(self):
print ( ("%9.2f" % ((t-self.t0)*1000)) + " ms for: " + remark )
self.t0 = t
#******************************************************************************
def read_in_ktolist (self,ktodirs0):
bezeichner = {}
print("")
self.mark("")
self.mark("A. Start sync")
ktodirs = []
for ktodir in ktodirs0:
if "EXCLUDE" in ktodir:
continue
ktodirs.append(re.sub(r"([^\\\/])$","\\1/",ktodir))
main_dir = self.common_string(ktodirs) # das tiefste uebergeordnete Directory ermitteln
db_dir = re.sub(r"^(.*)[\\\/].*?$","\\1",main_dir) # das erste gemeinsam erreichbare Datenbankfile ermitteln
while (0 == 0):
db_file = glob.glob(db_dir+"/*.db")
print(db_dir,db_file,len(db_file))
if len(db_file) == 1:
break
if len(db_file) > 1:
fehler = "ERROR: dbfile in " + db_dir + " is ambiguous"
print(fehler)
return(fehler)
if os.path.isfile(db_dir + "/fibu.py"): # oberstes Directory erreicht
db_file = None # kein Datenbankfile
break
db_dir = os.path.abspath(db_dir+"/..")
self.generated_in = None
if not db_file:
print(" ... no database found, so create it here ...")
db_file = [main_dir+"/__cache__.db"]
self.generated_in = main_dir
if main_dir in ktodirs:
ktodirs.remove(main_dir)
ktodirs.insert(0,main_dir)
self.generate_db(db_file[0])
self.mark("B. DB-File " + db_file[0])
ktolist = []
for dir in ktodirs: # jetzt die Kontoobjekte erzeugen
if not os.path.isdir(dir):
continue
print(dir)
kto = procpy.fibu.kto.Kto() # jeweils ein Konto-Objekt daraus machen
kto.find_ukto_and_interval(dir) # aus dem Pfad die Konto-ID und das Gueltigkeitsintervall herauslesen
kto.rulenr = 0 # Anzahl der Runden, die das Konto durchlaeuft
kto.rule = glob.glob(dir+"/rule.py")
kto.dir = dir
ktolist.append(kto)
if self.generated_in == main_dir:
self.generated_in = kto
self.mark("C. Generate Kto objects")
for kto in ktolist:
ktofile = glob.glob(kto.dir+"/*.kto") # jetzt den jeweiligen Kontotext einlesen
if len(ktofile) == 1: # es darf hoechstens ein kto-File im Konto-Verzeichnis geben
kto.file = kto.dir + "/" + kto.filename + ".kto"
try:
ktotext = open(ktofile[0]).read()
except:
print(ktofile[0] + " could not being opened.")
if not ktofile[0] == kto.file:
os.rename(ktofile[0],kto.file)
elif len(ktofile) == 0:
kto.file = kto.dir + "/" + kto.filename + ".kto"
if not os.path.isdir(kto.dir):
os.mkdir(kto.dir)
open(kto.file,"w").write("NEWKTO\n")
ktotext = "NEWKTO\n"
else: # im Fehlerfall mit einer Fehlermeldung Funktion beenden
fehler = "ERROR: ktofile in " + kto.dir + " is ambiguous"
print(fehler)
return(fehler)
kto.parse_ktotext(ktotext,bezeichner) # wenn das so ist, den Kontotext ins Konto-Objekt einlesen
self.mark(" > " + os.path.relpath(kto.file,"."))
self.mark("D. Parse kto files")
if self.generated_in:
print("generated in: " + self.generated_in.ukto)
self.generated_in.import_to_db(self.dbh)
# self.generated_in.db0 = self.generated_in.db
for kto in ktolist:
kto.id_new = kto.kto_id(kto.dir)
# print(kto.ukto)
m = re.search(r"^[^\n]+\(([A-Za-z0-9\_\+\-]{6})([A-Za-z0-9\_\+\-]{6})\)",kto.ktotext)
kto.id = "xxxxxx"
kto.db0 = "xxxxxx"
if m: # and not kto == self.generated_in:
kto.id = m.group(1) # der abgespeicherte Kto-Hash
kto.db = m.group(2) # der Kto-Hash des Parents
if not kto == self.generated_in:
continue
else:
kto.db0 = kto.db
if kto.ktotext[0:6] == "NEWKTO":
kto.id = kto.id_new
kto.db = "yyyyyy"
continue
buchungen = kto.export_from_db(self.dbh) # Kontoinhalt aus der Datenbank auslesen
kto.buchungen = buchungen
kto.db = kto.db_id(buchungen)
return(ktolist,main_dir,bezeichner)
# print("A",m,kto.ukto,kto.id,kto.db,"---",kto.id_new)
#******************************************************************************
def sync3 (self,ktodirs0):
(ktolist,main_dir,bezeichner) = self.read_in_ktolist(ktodirs0)
self.mark("E. Evaluate kto hashes")
fehlerliste = []
rounds = 0
while (0 == 0):
rounds = rounds + 1
print("\n\n*****************************\nROUND: " + ("%2u" % rounds) + "\n---------\n\n")
all_synced = True
for kto in ktolist:
if kto in fehlerliste:
continue
# print(kto.ukto,kto.rule)
buchungen = kto.export_from_db(self.dbh)
# print(buchungen)
kto.db_new = kto.db_id(buchungen)
print(kto.ukto,"---",kto.id,kto.db,"---",kto.id_new,kto.db_new)
if kto.db_new == kto.db and kto.id_new == kto.id:
continue
elif not kto.db_new == kto.db and kto.id_new == kto.id: # or self.synchronized:
print(" ... write " + kto.ukto)
kto.buchungen = buchungen
if os.path.isfile(kto.filename+".ext"):
kto.ext = 1
saldo = kto.write_ktotext(bezeichner,self.sortfkt) # Kontotext erzeugen
# print(kto.ukto,"KTOTEXT:",kto.ktotext)
kto.db = kto.db_new
kto.id = kto.kto_id(kto.dir)
open(kto.file,"w").write(
("%-40s" % kto.filename) + " (" + kto.id + kto.db_new + ")" +
# " Saldo: " +
("%15.2f" % saldo) +
"\n\n" + kto.ktotext )
if os.path.isfile(kto.filename+".csv"):
open(kto.filename+".csv","w").write(kto.write_csv(kto.filename))
if os.path.isfile(kto.filename+".ext"):
open(kto.filename+".ext","w").write(kto.ext_salden)
if len(kto.rule) == 1:
m = re.search(r"^(.*)[\\\/](.*)$","/"+kto.rule[0])
if m:
rule1 = m.group(2)
print(" ... run rule " + str(kto.rulenr) +" for " + kto.ukto)
os.system("chdir " + kto.dir + "; cd " + kto.dir + "; " +
self.pythoncall + " " + rule1 + " " + str(kto.rulenr) )
kto.rulenr = kto.rulenr + 1
text1 = open(kto.file).read() + "\n X"+str(random.randint(100000,999999))+"\n"
# the random string is appended to avoid that the rule can hang in a loop
# because it reproduces always the same faulty file
kto.parse_ktotext( text1, bezeichner )
kto.id_new = kto.kto_id(kto.dir)
all_synced = False
if not kto.id_new == kto.id: # gleich kto neu schreiben
kto.delete_from_db(self.dbh)
kto.import_to_db(self.dbh)
kto.db = "yyyyyy" #
kto.id = kto.id_new #
elif kto.db_new == kto.db and not kto.id_new == kto.id or self.synchronized:
print(" ... update " + kto.ukto)
kto.delete_from_db(self.dbh)
kto.parse_ktotext( open(kto.file).read(), bezeichner )
kto.import_to_db(self.dbh)
self.dbh.commit()
kto.db = "yyyyyy"
kto.id = kto.id_new
all_synced = False
elif not kto.db_new == kto.db and not kto.id_new == kto.id:
fehler = "ERROR: Conflict in " + kto.dir
print(fehler)
fehlerliste.append(kto)
self.dbh.commit()
self.mark("F"+str(rounds)+". Round " + str(rounds) + " finished.")
if all_synced:
print(" ... all accounts synced")
break
self.dbh.commit()
self.mark("G. All accounts synced. DB Commit")
print("")
if self.generated_in:
print("Korrektur")
text = open(self.generated_in.file).read().split("\n")
m = re.search(r"^(.*\()([A-Za-z0-9\_\+\-]{6})([A-Za-z0-9\_\+\-]{6})(\).*)$",text[0])
if m:
text[0] = m.group(1) + m.group(2) + self.generated_in.db0 + m.group(4)
# text[0] = m.group(1) + "xxxxxx" + m.group(3) + m.group(4)
open(self.generated_in.file,"w").write("\n".join(text))
# os.unlink(main_dir+"/__cache__.db")
if os.path.isfile(main_dir+"/rounds"):
open(main_dir+"/rounds","w").write(str(rounds)+"\n")
return(fehlerliste)
#******************************************************************************
def liqui (self,templatefile,ktofiles):
# 1. Einlesen der Kontenfiles
ktolist = {}
for ktofile in ktofiles:
ktotext = open(ktofile).read()
betraege = []
for zeile in ktotext.split("\n"):
m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\S+) +(\S*) +(\S+) +(\-?\d+\.\d\d) +(.*)$",zeile)
if not m:
continue
betraege.append( [m.group(1),float(m.group(2)),m.group(4)] )
m = re.search(r"^(.*)[\\\/](.*)$","./"+ktofile)
ktolist[m.group(2)] = betraege
# 2. Die Zeile feststellen, aus der die Zeitraeume stammen
template = open(templatefile).read()
template = re.sub(r"^(.*)\s*$","\\1",template,re.DOTALL)
template = re.sub(r" *; *",";",template,99999999)
for zeile in template.split("\n"):
zeitraeume = []
lastentry = None
entries = zeile.split(";")
for entry in entries:
erg = self.zeitraum(entry)
if lastentry and not erg:
break
lastentry = erg
zeitraeume.append(entry)
if len(zeitraeume) == len(entries) and not lastentry == None:
break
# 3. Jetzt jede Zeile durchgehen
text = []
for zeile in template.split("\n"):
entries = zeile.split(";")
if len(entries) < 2:
continue
ktopattern = entries[0]
ukto = ":" + entries[1]
summe = entries[2]
# print(zeile,ukto,summe)
zaehler = -1
zeile1 = []
for entry in entries:
zaehler = zaehler + 1
erg = None
if zaehler < len(zeitraeume) :
erg = self.zeitraum(zeitraeume[zaehler])
# print(zeile,erg,entry)
if erg:
entry1 = 0.00
startdatum = erg[0]
enddatum = erg[1]
buchungen = []
for ktofile in ktolist:
# print(ktofile,ktopattern)
if ktopattern.lower() in ktofile.lower():
buchungen.append( ktolist[ktofile] )
if len(buchungen) == 0:
entry1 = entry
else:
if not len(buchungen) == 1:
print("Fehler!",len(buchungen))
return()
buchungen = buchungen[0]
for buchungszeile in buchungen:
datum = buchungszeile[0]
betrag = buchungszeile[1]
ukto1 = ":" + buchungszeile[2]
if datum > enddatum:
continue
if datum < startdatum and summe == "":
continue
if not ukto in ukto1:
continue
entry1 = entry1 + betrag
entry1 = "%3.2f" % entry1
else:
entry1 = entry
zeile1.append(entry1)
zeile2 = ";".join(zeile1)
text.append(zeile2)
text = "\n".join(text) + "\n"
print(text)
#******************************************************************************
def zeitraum (self,entry):
m = re.search(r"^(\d\d\d\d)(\d\d)$",entry)
if m:
return([entry+"00",entry+"99"])
return(None)
#******************************************************************************
def common_string (self,elements):
main_el = elements[0]
for el in elements: # jetzt das tiefste uebergeordnete Directory ermitteln
main_el = main_el[0:len(el)]
el1 = el[0:len(main_el)]
while (0 == 0):
if el1 == main_el:
break
el1 = el1[:-1]
main_el = main_el[:-1]
return(main_el)
#******************************************************************************
def sort (self,pattern,file):
if pattern == "..":
self.sort_numbers(file)
return()
text = open(file).read()
text_match = []
text_rest = []
for zeile in text.split("\n"):
if re.search(pattern,zeile,re.IGNORECASE):
text_match.append(zeile)
else:
text_rest.append(zeile)
text = "\n".join(text_rest) + "\n" + "\n".join(text_match) + "\n"
if os.path.isfile(file+"~"):
os.unlink(file+"~")
# os.rename(file,file+"~")
open(file,"w").write(text)
#******************************************************************************
def sort_numbers (self,file):
text = open(file).read()
text_match = []
text_rest = []
for zeile in text.split("\n"):
m = re.search(r"\d\d\d\d\d\d\d\d +\-?(\d+\.\d\d) ",zeile)
if m:
text_match.append([zeile,float(m.group(1))])
else:
text_rest.append(zeile)
text_match.sort(key=lambda x:x[1])
text_match1 = []
for tt in text_match:
text_match1.append(tt[0])
text = "\n".join(text_rest) + "\n" + "\n".join(text_match1) + "\n"
if os.path.isfile(file+"~"):
os.unlink(file+"~")
# os.rename(file,file+"~")
open(file,"w").write(text)
#*************************************************************************************
def find_quittung (self,daydiff,file,pattern=""):
buchungen = []
text = []
text1 = []
ktotext = open(file).read()
for zeile in ktotext.split("\n"):
m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\-?\d+\.\d\d) +(\S+) +(\S+) +(\-?\d+\.\d\d) +(.*)",zeile)
if not m:
text.append(zeile)
continue
else:
datum = m.group(1)
betrag = m.group(2)
ktoa = m.group(3)
ktob = m.group(4)
remark = m.group(6)
buchungen.append([0,datum,betrag,ktoa,ktob,remark,zeile])
day0 = datetime.datetime.strptime("19700101","%Y%m%d")
try:
day = datetime.datetime.strptime(datum,"%Y%m%d")
except:
day = datetime.datetime.strptime(datum[0:6]+"28","%Y%m%d")
buchungen[-1][0] = (day-day0).days
while (0 == 0):
if buchungen[-1][0] - buchungen[0][0] < int(daydiff):
break
text.append(buchungen.pop(0)[-1])
'''
if abs(float(buchung[2])) == abs(float(buchungen[-1][2])):
buchung1 = buchung
buchung2 = buchungen[-1]
common_kto1 = ""
common_kto2 = ""
m1 = re.search("("+pattern+")",buchung1[-1])
if m1:
common_kto1 = m1.group(1)
m2 = re.search("("+pattern+")",buchung2[-1])
if m2:
common_kto2 = m2.group(1)
print(common_kto1,common_kto2)
print(buchung1[-1])
print(buchung2[-1])
if not common_kto1 == common_kto2: # matching!
text1.append("")
if pattern in buchung2[-1]:
buchung0 = buchung1
buchung1 = buchung2
buchung2 = buchung0
betrag1 = buchung1[2]
ktoa1 = buchung1[3]
ktob1 = buchung1[4]
if not betrag1[0] == "-":
betrag1 = "-" + betrag1
ktox = ktoa1
ktoa1 = ktob1
ktob1 = ktox
betrag2 = buchung1[2]
ktoa2 = buchung1[3]
ktob2 = buchung1[4]
if not betrag2[0] == "-":
betrag2 = "-" + betrag2
ktox = ktoa2
ktoa2 = ktob2
ktob2 = ktox
if ktob1 == standard_kto:
ktob1 = ktob2
if ktob2 == standard_kto:
ktob2 = ktob1
ktob2 = ktoa1
zeile1 = buchung1[1] + " " + betrag1 + " " + ktoa1 + " " + ktob1 + " 0.00 " + buchung1[5]
zeile2 = buchung2[1] + " " + betrag2 + " " + ktoa2 + " " + ktob2 + " 0.00 " + buchung2[5]
text1.append(zeile1)
text1.append(zeile2)
'''
standard_kto = "-13-9999"
for buchung in buchungen[:-1]:
if abs(float(buchung[2])) == abs(float(buchungen[-1][2])):
buchung1 = buchung
buchung2 = buchungen[-1]
common_kto1 = ""
common_kto2 = ""
m1 = re.search("("+pattern+")",buchung1[-1])
if m1:
common_kto1 = m.group(1)
m2 = re.search("("+pattern+")",buchung2[-1])
if m2:
common_kto2 = m.group(1)
if not common_kto1 == common_kto2:
text1.append("")
if pattern in buchung2[-1]:
buchung0 = buchung1
buchung1 = buchung2
buchung2 = buchung0
betrag1 = buchung1[2]
ktoa1 = buchung1[3]
ktob1 = buchung1[4]
if not betrag1[0] == "-":
betrag1 = "-" + betrag1
ktox = ktoa1
ktoa1 = ktob1
ktob1 = ktox
betrag2 = buchung2[2]
ktoa2 = buchung2[3]
ktob2 = buchung2[4]
if not betrag2[0] == "-":
betrag2 = "-" + betrag2
ktox = ktoa2
ktoa2 = ktob2
ktob2 = ktox
if ktob1 == standard_kto:
ktob1 = ktob2
if ktob2 == standard_kto:
ktob2 = ktob1
ktob2 = re.sub("QUIT","ZAHL",ktoa1)
zeile1 = buchung1[1] + " " + betrag1 + " " + ktoa1 + " " + ktob1 + " 0.00 " + buchung1[5]
zeile2 = buchung2[1] + " " + betrag2 + " " + ktoa2 + " " + ktob2 + " 0.00 " + buchung2[5]
if ktoa1 == ktob1 or ktoa2 == ktob2:
text1.append(buchung1[-1])
text1.append(buchung2[-1])
else:
text1.append("# " + buchung1[-1])
text1.append("# " + buchung2[-1])
text1.append(zeile1)
text1.append(zeile2)
else:
text.append(buchung1[-1])
text.append(buchung2[-1])
buchungen.pop()
buchungen.remove(buchung)
break
for buchung in buchungen:
text.append(buchung[-1])
ktotext = "\n".join(text) + "\n" + "\n".join(text1) + "\n"
open(file,"w").write(ktotext)