Browse Source

Add script to help reconcile with gnucash

Daniel Sheffield 3 years ago
parent
commit
8eef9a3e63
1 changed files with 113 additions and 0 deletions
  1. 113 0
      reconcile.py

+ 113 - 0
reconcile.py

@@ -0,0 +1,113 @@
+#!/usr/bin/python3
+from datetime import datetime
+from dateutil.parser import parse as parse_time
+import itertools
+import gnucash
+import sys
+
+def _unwrap_list(root, blacklist):
+  for i in map(lambda x: [
+      (':'.join([x[0], c.name]), True) for c in root.get_children() 
+    ] if x[1] else [ x ], blacklist):
+    for j in i: yield j
+
+def _accounts(root, path, blacklist, whitelist):
+  #print('blacklist1: {}'.format(blacklist))
+  #print(f'whitelist1 ({root.name}: {whitelist}')
+  
+  if root.name in [ w[0] for w in whitelist ] and \
+     root.name not in [ b[0] for b in blacklist ]:
+         yield path, root
+  blacklist = [b for b in itertools.chain(
+    _unwrap_list(root, filter(lambda x: (x[0] == root.name), blacklist)),
+    filter(lambda x: not (x[0] == root.name), blacklist)
+  )]
+  whitelist = [b for b in itertools.chain(
+    _unwrap_list(root, filter(lambda x: (x[0] == root.name), whitelist)),
+    filter(lambda x: not (x[0] == root.name), whitelist)
+  )]
+  path = [*path, root.name]
+  prefix = '{}:'.format(root.name)
+  #print('root: {}'.format(root.name))
+  #print('prefix: {}'.format(prefix))
+  #print('path: {}'.format(':'.join(path)))
+  blacklist = [
+    b for b in map(
+      lambda x: (x[0][len(prefix):], x[1]), filter(
+        lambda x: x[0].startswith(prefix), blacklist
+      )
+    )
+  ]
+  whitelist = [
+    b for b in map(
+      lambda x: (x[0][len(prefix):], x[1]), filter(
+        lambda x: x[0].startswith(prefix), whitelist
+      )
+    )
+  ]
+  #print('blacklist: {}'.format(blacklist))
+  for a in root.get_children():
+    for p, c in _accounts(a, path, blacklist, whitelist):
+      yield p, c
+
+def _path(account):
+  if account.get_parent() is not None: return ':'.join([_path(account.get_parent()), account.name])
+  else: return account.name
+
+def _iter(root, blacklist, whitelist, _from, _to):
+  for p, a in _accounts(root, [], blacklist, whitelist):
+    apath = _path(a)
+    s = dict()
+
+    for t in a.GetSplitList():
+      tpath = _path(t.account)
+      if datetime.fromtimestamp(_to) > t.parent.GetDate() >= datetime.fromtimestamp(_from):
+        #if apath.startswith('Root Account:Trading'):
+        #  if tpath.startswith('Root Account:Trading:CURRENCY'):
+        #    s+=t.GetValue().num()
+        #elif tpath == apath:
+        if tpath == apath:
+          #print(t.parent.GetDescription())
+          desc = t.parent.GetDescription()
+          if desc not in s:
+              s[desc] = 0
+          #print(t.parent.GetNotes())
+          s[desc]+=t.GetValue().num()
+        #if apath.startswith('Assets'): s+= t.GetValue().num()
+        #elif apath.startswith('Liabilities'): s-= t.GetValue().num()
+        #if apath.startswith('Income'): s+= t.GetValue().num()
+    yield p, a.name, s
+    #for t in a.GetSplitList():
+    #  print(t.parent.GetDate(), type(t.parent.GetDate())) 
+
+if __name__ == '__main__':
+  args = sys.argv
+  session = gnucash.Session(args[1])
+  try:
+    blacklist = [
+        # name, also_children
+        ('Root Account:Assets', True),
+        ('Root Account:Liabilities', True),
+        ('Root Account:Equity', True),
+        ('Root Account:Income', True),
+    ]
+    whitelist = [
+        # name, also_children
+        ('Root Account:Expenses:NZD:Food:Groceries', True),
+    ]
+    _from = int(parse_time(args[2]).timestamp())# 1554030000 # 1 April 2019 as seconds since epoch
+    _to = int(parse_time(args[3]).timestamp())
+    _interval = int(args[4])*60*60*24
+    for f, t in [ (i, i+_interval) for i in range(_from, _to, _interval)]:
+      ts = datetime.fromtimestamp(f)
+      to = datetime.fromtimestamp(t)
+      tot = 0
+      for p,n,s in _iter(session.book.get_root_account(), blacklist, whitelist, f, t):
+        account_name = ':'.join([*p, n])
+        for k in sorted(s.keys()):
+            print(f'{ts} {k:35s}: {s[k]/100:>06.2f}')
+            tot += s[k]
+      if tot > 0:
+          print(f'{to} {str("TOTAL"):35s}: {tot/100:>06.2f}')
+  finally:
+    session.end()