commit fe1638302dd048dd8be32f729ccbed9b5190ebd3
parent 429b73bec76553ec64e9053fe0c0b6fb768aef7e
Author: lash <dev@holbrook.no>
Date: Sat, 21 Mar 2026 19:46:59 -0600
Enable account listing display and match filters
Diffstat:
2 files changed, 88 insertions(+), 17 deletions(-)
diff --git a/dummy/tests/account.py b/dummy/tests/account.py
@@ -4,7 +4,7 @@ import unittest
import os
from usawa import UnitIndex
-from usawa.account import AccountIndex
+from usawa.account import AccountIndex, AccountType, AccountDisplay
from usawa.error import AccountError
logging.basicConfig(level=logging.DEBUG)
@@ -29,8 +29,8 @@ class TestAccount(unittest.TestCase):
idx.add('asset/bar/baz-', sym='FOO')
with self.assertRaises(AccountError):
idx.add('asset/foo/bar', sym='BAZ')
- self.assertFalse(idx.check('BAR', 'liability/foo/baz'))
- self.assertTrue(idx.check('FOO', 'liability/bar/baz'))
+ self.assertFalse(idx.check('BAR', AccountType.liability, 'foo/baz'))
+ self.assertTrue(idx.check('FOO', AccountType.liability, 'bar/baz'))
def test_account_list(self):
@@ -39,9 +39,41 @@ class TestAccount(unittest.TestCase):
idx.add('liability/bar/baz', sym='FOO')
idx.add('asset/foo/baz', sym='BAR')
v = list(idx)
- logg.debug('results {}'.format(v))
self.assertEqual(len(v), 3)
+ def test_account_filter(self):
+ idx = AccountIndex(self.uidx)
+ idx.add('asset/bar/bar', sym='FOO')
+ idx.add('liability/bar/baz', sym='FOO')
+ idx.add('asset/foo/baz', sym='BAR')
+ idx.add('asset/xyzzy', sym='BAR')
+ idx.set_filter(sym='FOO')
+ v = list(idx)
+ self.assertEqual(len(v), 2)
+ idx.set_filter(sym='BAR')
+ v = list(idx)
+ self.assertEqual(len(v), 2)
+ idx.set_filter(sym='FOO', typ=AccountType.liability)
+ v = list(idx)
+ self.assertEqual(len(v), 1)
+ idx.set_filter(sym='BAR', typ=AccountType.asset)
+ v = list(idx)
+ self.assertEqual(len(v), 2)
+
+
+ def test_account_display(self):
+ idx = AccountIndex(self.uidx)
+ idx.add('asset/bar/baz', sym='FOO')
+ v = list(idx)
+ self.assertEqual(v[0], 'FOO.asset/bar/baz')
+ idx.set_filter(display=AccountDisplay.typ)
+ v = list(idx)
+ self.assertEqual(v[0], 'asset/bar/baz')
+ idx.set_filter(display=AccountDisplay.path)
+ v = list(idx)
+ self.assertEqual(v[0], 'bar/baz')
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/dummy/usawa/account.py b/dummy/usawa/account.py
@@ -13,19 +13,19 @@ def check_path_parts(path):
raise AccountError('invalid part: ' + v)
#return True
typ = getattr(AccountType, parts[0].lower())
- return (typ, parts,)
+ return (typ, parts[1:],)
def from_account_path(p, sym=None, typ=None):
if sym != None:
p = sym + '.' + p
o = p.split('.')
- logg.debug('have {} {} {}'.format(p, sym, typ))
if len(o) != 2:
raise ValueError('account path should have zero or one symbol specifier')
sym = o[0]
- o = check_path_parts(o[1])
- typ = o[0]
+ if typ == None:
+ o = check_path_parts(o[1])
+ typ = o[0]
path = o[1]
return (sym, typ, path,)
@@ -40,6 +40,12 @@ class AccountType(enum.Enum):
export = 'Export'
+class AccountDisplay(enum.IntEnum):
+ full = 0
+ typ = 1
+ path = 2
+
+
class Account:
path_parser = from_account_path
@@ -59,8 +65,9 @@ class Account:
def to_path(self):
- path = self.segments.join('/')
- path = '{}.{}/{}'.format(self.sym, self.typ, path)
+ path = '/'.join(self.segments)
+ path = '{}.{}/{}'.format(self.sym, self.typ.value.lower(), path)
+ return path
class AccountIndex:
@@ -71,6 +78,7 @@ class AccountIndex:
self.locked = False
#self.validate = pathvalidator
self.iterval = None
+ self.iterfilter = None
def add(self, path, sym=None, typ=None):
@@ -85,6 +93,8 @@ class AccountIndex:
self.accounts[sym] = []
elif path in self.accounts[sym]:
logg.debug('Ignoring duplicate account: {}:{}'.format(sym, path))
+ path = account.to_path()
+ logg.info('add account {}'.format(path))
self.accounts[sym].append(path)
@@ -92,28 +102,57 @@ class AccountIndex:
self.locked = True
- def check(self, sym, path):
+ def check(self, sym, typ, path):
+ s = '{}.{}/{}'.format(sym, typ.value.lower(), path)
try:
- return path in self.accounts[sym]
+ return s in self.accounts[sym]
except KeyError:
return False
- def __iter__(self):
+ def set_filter(self, sym=None, typ=None, display=AccountDisplay.full):
+ if typ != None:
+ typ = typ.value
+ self.iterfilter = (sym, typ, display,)
+
+
+ def __iter__(self, fltr=None):
+ self.iterval = None
keys = list(self.uidx.syms())
keys.sort()
+ fltr = self.iterfilter
for k in keys:
- for v in self.accounts[k]:
+ if fltr != None:
+ if fltr[0] != None:
+ if fltr[0] != k:
+ continue
+ accounts = self.accounts.get(k)
+ if accounts == None:
+ continue
+ for v in accounts:
+ path = v
+ (sym, v) = v.split('.', maxsplit=1)
+ (typ, v) = v.split('/', maxsplit=1)
+ if fltr != None:
+ if fltr[1] != None:
+ if typ.casefold() != fltr[1].casefold():
+ continue
if self.iterval == None:
self.iterval = []
- self.iterval.append(k + '/' + v)
+ if fltr != None:
+ if fltr[2] != AccountDisplay.full:
+ path = v
+ if fltr[2] == AccountDisplay.typ:
+ path = typ + '/' + path
+ self.iterval.append(path)
return self
def __next__(self):
+ if self.iterval == None:
+ raise StopIteration()
if len(self.iterval) == 0:
- self.iverval = None
+ self.iterval = None
raise StopIteration()
v = self.iterval.pop(0)
return v
-