usawa

Signed, immutable accounting.
Log | Files | Refs | Submodules | LICENSE

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:
Adummy/tests/index.py | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdummy/usawa/entry.py | 6++++++
Adummy/usawa/index/account.py | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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