usawa

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

commit 3bf0b89cc80dc2a4a076728b4a32ce92e97f5630
parent 53dde933bd84750594973c33794a6aa9ab2300ff
Author: lash <dev@holbrook.no>
Date:   Sat,  4 Apr 2026 21:46:09 -0600

WIP Flexible virt real definition, implement for account

Diffstat:
Mdummy/usawa/account.py | 16++++++++--------
Mdummy/usawa/data/schema.xsd | 2++
Mdummy/usawa/ledger.py | 10+++++++---
Mdummy/usawa/unit.py | 38++++++++++++++++++++++++++++++++++----
4 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/dummy/usawa/account.py b/dummy/usawa/account.py @@ -17,7 +17,7 @@ def check_path_parts(path): return (typ, parts[1:],) -def from_account_path(p, sym=None, typ=None): +def from_account_path(p, sym=None, typ=None, virt=False): if sym != None: p = sym + '.' + p o = p.split('.') @@ -54,7 +54,7 @@ class Account: path_parser = from_account_path - def __init__(self, sym, typ, segments): + def __init__(self, sym, typ, segments, virt=False): if not isinstance(typ, AccountType): raise ValueError('invalid account type: ' + typ) self.sym = sym @@ -62,12 +62,13 @@ class Account: if isinstance(segments, str): segments = [segments] self.segments = segments + self.virt = False @staticmethod - def from_path(path, sym=None, typ=None): - o = Account.path_parser(path, sym=sym, typ=typ) - return Account(o[0], o[1], o[2]) + def from_path(path, sym=None, typ=None, virt=False): + o = Account.path_parser(path, sym=sym, typ=typ, virt=virt) + return Account(o[0], o[1], o[2], virt=virt) def to_path(self, display=AccountDisplay.full): @@ -112,12 +113,11 @@ class AccountIndex: break o.add(v.strip()) f.close() -# return AccountIndex.from_io(f, closer=f.close) return o - def add(self, path, sym=None, typ=None): - account = Account.from_path(path, sym=sym, typ=typ) + def add(self, path, sym=None, typ=None, virt=False): + account = Account.from_path(path, sym=sym, typ=typ, virt=virt) try: sym = self.uidx.sym(account.sym) except KeyError: diff --git a/dummy/usawa/data/schema.xsd b/dummy/usawa/data/schema.xsd @@ -79,8 +79,10 @@ <xs:complexType name="Unit"> <xs:sequence> <xs:element name="precision" type="xs:positiveInteger" maxOccurs="1" minOccurs="1" /> + <xs:element name="virt" type="xs:string" maxOccurs="unbounded" minOccurs="0" /> </xs:sequence> <xs:attribute name="sym" type="xs:string" /> + <xs:attribute name="real" type="xs:boolean" /> </xs:complexType> <xs:complexType name="Rate"> diff --git a/dummy/usawa/ledger.py b/dummy/usawa/ledger.py @@ -139,13 +139,16 @@ class RunningTotal: :rtype: usawa.UnitIndex """ @staticmethod - def from_tree(tree, unitindex): + def from_tree(tree, unitindex, real=False): unit = tree.get('unit') asset = int(tree.find('asset', namespaces=nsmap()).text) liability = int(tree.find('liability', namespaces=nsmap()).text) income = int(tree.find('income', namespaces=nsmap()).text) expense = int(tree.find('expense', namespaces=nsmap()).text) - return RunningTotal(unit, unitindex, asset=int(asset), liability=int(liability), income=int(income), expense=int(expense)) + o = RunningTotal(unit, unitindex, asset=int(asset), liability=int(liability), income=int(income), expense=int(expense)) + if real: + o.set_real() + return o @@ -243,6 +246,7 @@ class Ledger(UsawaElement): :type wallet: usawa.Wallet :todo: Add warnings for ignored parameters :todo: Remove enclosing array for entries + :todo: Implement real and virt unit indices for running totals """ def __init__(self, unitindex, acl=None, serial=0, base=DEFAULTPARENT, topic=None, src=None, wallet=None): self.uidx = unitindex @@ -597,7 +601,7 @@ class Ledger(UsawaElement): for v in part.iter(NSPREFIX + 'real'): sym = v.get('unit') - o = RunningTotal.from_tree(v, unitindex) + o = RunningTotal.from_tree(v, unitindex, real=True) ledger.running[o.sym] = o logg.debug('ledger running total {} (real): {}'.format(o.sym, o)) diff --git a/dummy/usawa/unit.py b/dummy/usawa/unit.py @@ -28,7 +28,7 @@ class UnitIndex: :param precision: The decimal precision of the base unit. Default is 2. :type precision: int """ - def __init__(self, base=None, precision=None): + def __init__(self, base=None, precision=None, virt=False): self.detail = {} self.rate = {} self.base = None @@ -38,6 +38,10 @@ class UnitIndex: self.detail = {base: precision} self.set_rate(base, UnitIndex.default_exchange) self.base = base + self.real = {base: True} + self.virt = {} + if virt: + self.virt[base] = True def clone(self): @@ -87,9 +91,15 @@ class UnitIndex: :param ex: The exchange rate of the unit, relative to the base unit. Default is 1000000000 (1.0). :type ex: int or float """ - def add(self, sym, precision=2, rate=1000000000): + def add(self, sym, precision=2, rate=1000000000, virt=None): + if self.detail.get(sym) != None: + raise AttributeError('sym already added') self.detail[sym] = precision self.set_rate(sym, rate) + if virt: + self.virt[sym] = True + else: + self.real[sym] = True """Create a unit index object from XML. @@ -109,9 +119,15 @@ class UnitIndex: base = tree.get('base') r = UnitIndex(base) for o in tree.iter(NSPREFIX + 'unit'): + sym = o.get('sym') + if o.get('real'): + r.real[sym] = True precision = int(o.find('precision', namespaces=nsmap()).text) - logg.debug('add unit {} precision {}'.format(o.get('sym'), precision)) - r.detail[o.get('sym')] = precision + virt = o.find('virt', namespaces=nsmap()) + if virt != None: + r.virt[sym] = True + logg.debug('add unit {} precision {}'.format(sym, precision)) + r.detail[sym] = precision r.check() return r @@ -161,6 +177,14 @@ class UnitIndex: return list(self.detail.keys()) + def reals(self): + return list(self.real.keys()) + + + def virts(self): + return list(self.virt.keys()) + + """Generate a string representing the decimal equivalent of the value to the precision of the unit. :param sym: The symbol to use precision for. @@ -291,9 +315,15 @@ class UnitIndex: for k in self.detail.keys(): unit = lxml.etree.SubElement(tree, 'unit') unit.set('sym', k) + if self.real.get(k): + unit.set('real', '1') o = lxml.etree.SubElement(unit, 'precision') o.text = str(self.detail[k]) unit.append(o) + if self.virt.get(k): + o = lxml.etree.SubElement(unit, 'virt') + o.text = '1' + unit.append(o) tree.append(unit) return tree