commit f707050d8a67ebf9825035ebf1243a0d4ed3fc5c
parent d228482a4dc102002d7df8fbf2d65da4e467feae
Author: lash <dev@holbrook.no>
Date: Tue, 6 Jan 2026 13:43:52 +0100
Add cli arg defaults to interactive tool
Diffstat:
3 files changed, 104 insertions(+), 29 deletions(-)
diff --git a/dummy/usawa/constant.py b/dummy/usawa/constant.py
@@ -2,3 +2,4 @@ DEFAULTPARENT = b'\x00' * 64
NS = 'http://usawa.defalsify.org/'
NAMESPACES = {None: NS}
NSPREFIX = '{' + NS + '}'
+CATEGORIES = ['income', 'expense', 'asset', 'liability']
diff --git a/dummy/usawa/runnable/add.py b/dummy/usawa/runnable/add.py
@@ -5,6 +5,7 @@ import uuid
import datetime
from usawa import Ledger, Entry, EntryPart, DemoWallet, UnitIndex, load
+from usawa.constant import CATEGORIES
from usawa.store import LedgerStore
from whee.valkey import ValkeyStore
@@ -12,9 +13,51 @@ logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
+class Context:
+
+ def __init__(self):
+ self.unit = None
+ self.uidx = None
+ self.ref = None
+ self.description = None
+ self.src = [None, None, None]
+ self.dst = [None, None, None]
+ self.part = []
+
+
+ @staticmethod
+ def from_args(args):
+ ctx = Context()
+ ctx.unit = args.unit
+ ctx.uidx = UnitIndex(ctx.unit)
+ if args.description != None:
+ ctx.description = args.description
+ if args.r != None:
+ ctx.ref = args.r
+ else:
+ ctx.ref = str(uuid.uuid4())
+ ctx.src[0] = args.src_type
+ ctx.src[1] = args.src_account
+ ctx.dst[0] = args.dst_type
+ ctx.dst[1] = args.dst_account
+ return ctx
+
+ def validate(self):
+ for v in self.src:
+ if v == None:
+ raise ValueError('invalid src')
+ for v in self.dst:
+ if v == None:
+ raise ValueError('invalid dst')
+ #if self.topic == None:
+ # raise ValueError('invalid topic')
+ if self.ref == None:
+ raise ValueError('invalid ref')
+
+
def parse_type(v):
- if v not in ['expense', 'income', 'liability', 'asset']:
- raise ValueError('invalid type')
+ if v not in CATEGORIES:
+ raise ValueError('invalid type: ' + v)
return v
@@ -27,19 +70,40 @@ def parse_amount(uidx, sym, v):
return uidx.from_floatstring(sym, v)
+def input_or_default(prompt, default=None, postfix=': ', validate_fn=None):
+ if default != None:
+ postfix = ' [{}]'.format(default) + postfix
+ v = input(prompt + postfix)
+ if len(v) == 0:
+ if default == None:
+ raise ValueError('empty value and no default')
+ v = default
+ if validate_fn != None:
+ validate_fn(v)
+ return v
+
+
argp = argparse.ArgumentParser()
-argp.add_argument('-f', type=str, help='load ledger metadata from XML file')
-argp.add_argument('--topic', type=str, help='hexadecimal topic to load')
+argp.add_argument('-i', action='store_true', help='interactive edit')
+argp.add_argument('-r', type=str, help='external reference')
+argp.add_argument('-s', type=str, dest='src_account', default='general', help='source account')
+argp.add_argument('-t', type=str, dest='dst_account', default='general', help='destination account')
+argp.add_argument('--src-type', dest='src_type', type=str, choices=CATEGORIES, default='expense', help='source type')
+argp.add_argument('--dst-type', dest='dst_type', type=str, choices=CATEGORIES, default='asset', help='dest type')
+argp.add_argument('-d', '--description', type=str, help='interactive edit')
+argp.add_argument('-u', '--unit', type=str, default='BTC', help='Unit to use for transaction')
+argp.add_argument('--unit-precision', type=int, default=2, help='Unit precision')
+argp.add_argument('--unit-rate', type=float, default=1.0, help='Unit exchange rate')
+argp.add_argument('ledger_xml_file', type=str, help='load ledger metadata from XML file')
arg = argp.parse_args()
+ctx = Context.from_args(arg)
ledger = None
-unit = 'BTC'
-uidx = UnitIndex(unit)
logg.warning('hardcoding unit index default sym, need unitindex xml parser')
logg.warning('using default sym for all entries for now')
-if arg.f:
- ledger_tree = load(arg.f)
- ledger = Ledger.from_tree(ledger_tree, uidx)
+ledger_tree = load(arg.ledger_xml_file)
+uidx = UnitIndex.from_tree(ledger_tree)
+ledger = Ledger.from_tree(ledger_tree, uidx)
db = ValkeyStore('')
store = LedgerStore(db, ledger)
@@ -47,29 +111,39 @@ pk = store.get_key()
wallet = DemoWallet(privatekey=pk)
dt = datetime.datetime.now()
-v = input('Entry description: ')
-dsc = v
-pair = []
-amount = None
-for k in ['src', 'dst']:
- v = input('Entry {} type: '.format(k))
- typ = parse_type(v)
+def do_interactive(ctx):
+ v = input_or_default('Entry description', ctx.description)
+ dsc = v
+
+ amount = None
+ for k in ['src', 'dst']:
+ o = vars(ctx)
+ #v = input('Entry {} type: '.format(k))
+ v = input_or_default('Entry {} type'.format(k), o[k][0])
+ o[k][0] = parse_type(v)
+
+ v = input_or_default('Entry {} account'.format(k), o[k][1])
+ o[k][1] = parse_account(v)
+
+ if amount != None:
+ amount *= -1.0
+ else:
+ v = input_or_default('Entry {} amount'.format(k))
+ amount = parse_amount(uidx, ctx.unit, v)
+
+ o[k][2] = amount
+ ctx.part.append(EntryPart(o[k][0], o[k][1], o[k][2]))
- v = input('Entry {} account: '.format(k))
- account = parse_account(v)
-
- if amount != None:
- amount *= -1.0
- else:
- v = input('Entry {} amount: '.format(k))
- amount = parse_amount(uidx, unit, v)
+ ctx.ref = input_or_default('External ref', ctx.ref)
+ return ctx
- pair.append(EntryPart(typ, account, amount))
-ref = uuid.uuid4()
-logg.debug('generated ref {}'.format(ref))
+if arg.i:
+ ctx = do_interactive(ctx)
-entry = Entry(pair[0], pair[1], unit, ledger.serial, dt, parent=ledger.current(), description=dsc, ref=str(ref))
+ctx.validate()
+entry = Entry(ctx.part[0], ctx.part[1], ctx.unit, ledger.serial, dt, parent=ledger.current(), description=ctx.description, ref=ctx.ref)
entry.sign(wallet)
ledger.add_entry(entry)
+print(ledger.to_string())
diff --git a/dummy/usawa/unit.py b/dummy/usawa/unit.py
@@ -54,7 +54,7 @@ class UnitIndex:
for o in tree.iter(NSPREFIX + 'unit'):
logg.debug('add unit ' + o.get('sym'))
r.detail[o.get('sym')] = int(o.find('precision', namespaces=nsmap()).text)
- r.exchange[o.get('sym')] = int(o.find('ex', namespaces=nsmap()).text)
+ r.exchange[o.get('sym')] = int(o.find('exchange', namespaces=nsmap()).text)
r.check()
return r