commit 33c3013f56b691cf57ce406caf8516650b4cff65
parent 235d92f59e415a13191a19555041eefd4208b129
Author: lash <dev@holbrook.no>
Date: Fri, 1 May 2026 17:24:34 -0600
Add naïve reverse account entry index
Diffstat:
3 files changed, 147 insertions(+), 0 deletions(-)
diff --git a/dummy/tests/index.py b/dummy/tests/index.py
@@ -0,0 +1,78 @@
+import logging
+import datetime
+import unittest
+import os
+
+from whee.mem import MemStore
+
+from usawa import UnitIndex
+from usawa import Ledger, UnitIndex, EntryPart, Entry, DemoWallet, ACL, schema_path
+from usawa.store import LedgerStore
+from usawa.account import AccountIndex, AccountType, AccountDisplay
+from usawa.error import AccountError
+from usawa.index.account import EntryAccountIndex
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+testdir = os.path.realpath(os.path.dirname(__file__))
+
+class TestIndex(unittest.TestCase):
+
+
+ def setUp(self):
+ self.store = MemStore()
+ self.eidx = EntryAccountIndex()
+
+
+ def test_accountsindex(self):
+ s = 'FOO'
+ uidx = UnitIndex(s)
+ wallet = DemoWallet()
+ o = Ledger(uidx, wallet=wallet)
+ o.register_callback(self.eidx.entry_callback)
+ store = LedgerStore(self.store, ledger=o)
+ store.start()
+
+
+ x = EntryPart(s, 'income', 'foo', 1337, debit=True)
+ y = EntryPart(s, 'asset', 'foo', 1337)
+ v = Entry(o.peek(), datetime.datetime.now(), parent=o.current())
+ v.add_part(x)
+ v.add_part(y)
+ v.sign(wallet)
+ store.add_entry(v, update_ledger=True)
+
+ x = EntryPart(s, 'expense', 'bar', 42, debit=True)
+ y = EntryPart(s, 'liability', 'bar', 42)
+ v = Entry(o.peek(), datetime.datetime.now(), parent=o.current())
+ v.add_part(x)
+ v.add_part(y)
+ v.sign(wallet)
+ store.add_entry(v, update_ledger=True)
+
+ x = EntryPart(s, 'expense', 'bar', 42, debit=True)
+ y = EntryPart(s, 'liability', 'bar', 42)
+ v = Entry(o.peek(), datetime.datetime.now(), parent=o.current())
+ v.add_part(x)
+ v.add_part(y)
+ v.sign(wallet)
+ store.add_entry(v, update_ledger=True)
+
+ self.eidx.start('FOO.income/foo')
+ r = list(self.eidx)
+ self.assertEqual(len(r), 1)
+ entry = Entry.empty(serial=r[0])
+ entry = store.get_entry(entry)
+
+ self.eidx.start('FOO.expense/bar')
+ r = list(self.eidx)
+ self.assertEqual(len(r), 2)
+ for v in r:
+ entry = Entry.empty(serial=v)
+ entry = store.get_entry(entry)
+
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/dummy/usawa/entry.py b/dummy/usawa/entry.py
@@ -53,6 +53,10 @@ class EntryPart:
self.isdebit = debit
+ def account_path(self):
+ return self.unit + '.' + self.typ + '/' + self.account
+
+
"""Create object from an entry part defined as an XML tree.
The XML expected is the ledger/entry/data/debit or ledger/entry/data/credit (in schema, defined as the EntryPart complexType).
@@ -206,6 +210,8 @@ class Entry(UsawaElement):
self.description = description
self.debit = []
self.credit = []
+ self.srcs = self.debit
+ self.dsts = self.credit
self.lookup = None
self.lookup_algo = None
self.parts = []
diff --git a/dummy/usawa/index/account.py b/dummy/usawa/index/account.py
@@ -0,0 +1,63 @@
+import logging
+
+from usawa.account import Account
+
+logg = logging.getLogger('usawa.index.account')
+
+
+
+class EntryAccountIndex:
+
+ def __init__(self):
+ self.idx = {}
+ self.c = -1
+ self.v = None
+ self.it = None
+ self.k = None
+
+
+ def entry_callback(self, entry):
+ idx = {}
+ for ls in [entry.srcs, entry.dsts]:
+ for o in ls:
+ account_path = o.account_path()
+ try:
+ account = idx[account_path]
+ except KeyError:
+ logg.debug('path ' + account_path)
+ account = Account.from_path(account_path)
+ idx[account_path] = account
+ for k in idx.keys():
+ try:
+ self.idx[k]
+ except KeyError:
+ self.idx[k] = []
+ self.idx[k].append(entry.serial)
+ logg.debug('indexed {} -> {}'.format(k, entry.serial))
+ return True
+
+
+ def start(self, k):
+ self.k = k
+ return self.__iter__()
+
+
+ def __iter__(self):
+ self.v = self.idx[self.k]
+ self.c = 0
+ return self
+
+
+ def __next__(self):
+ v = None
+ try:
+ v = self.v[self.c]
+ except IndexError:
+ self.k = None
+ self.c = -1
+ self.v = None
+ self.it = None
+ raise StopIteration()
+ self.c += 1
+
+ return v