commit 7313df604c86ea1f1666a56b9cd8f97b692a5e1e
parent b64223a28abf188ba06699b29315684dd652d666
Author: lash <dev@holbrook.no>
Date: Fri, 3 Apr 2026 22:22:11 -0600
WIP enable multi account and amount interactive entry
Diffstat:
9 files changed, 407 insertions(+), 258 deletions(-)
diff --git a/dummy/setup.cfg b/dummy/setup.cfg
@@ -47,3 +47,4 @@ console_scripts =
usawa-import = usawa.runnable.import:main
usawa-export = usawa.runnable.export:main
usawa-revert = usawa.runnable.revert:main
+ usawa-asset = usawa.runnable.asset:main
diff --git a/dummy/tests/unit.py b/dummy/tests/unit.py
@@ -47,6 +47,9 @@ class TestUnit(unittest.TestCase):
self.assertEqual(v, 123)
v = self.uidx_default.from_floatstring('BAR', '1.2345')
self.assertEqual(v, 12345)
+ v = self.uidx_default.from_floatstring('FOO', '-3.390')
+ self.assertEqual(v, -3390)
+
def test_unit_rates(self):
diff --git a/dummy/usawa/balance.py b/dummy/usawa/balance.py
@@ -5,7 +5,7 @@ logg = logging.getLogger('usawa.balancer')
class Balancer:
- def __init__(self, unitindex):
+ def __init__(self, unitindex, value=None):
self.uidx = unitindex
self.r = 0
self.m = 0
diff --git a/dummy/usawa/context.py b/dummy/usawa/context.py
@@ -5,7 +5,7 @@ from whee.valkey import ValkeyStore
from whee.fs import FsStore
from usawa import DemoWallet, Ledger
from usawa.account import AccountIndex
-from usawa.store import LedgerStore, EntryStore, KeyStore
+from usawa.store import LedgerStore, EntryStore, KeyStore, AssetStore
from usawa.resolve.fs import FSResolver
logg = logging.getLogger('usawa.ctx')
@@ -42,7 +42,7 @@ class UsawaContext:
def get(self, k):
- return self.o[k]
+ return self.o.get(k)
def init(self, args, store_scope=None):
@@ -96,7 +96,7 @@ class UsawaContext:
if self.wallet != None:
raise AttributeError('wallet set')
if not self.signing:
- self.wallet = self.store.get_default_key(DemoWallet)
+ self.wallet = self.keystore.get_default_key(DemoWallet)
else:
ops = int(self.cfg.get('WALLET_OPSLIMIT', 0))
mem = int(self.cfg.get('WALLET_MEMLIMIT', 0))
@@ -116,11 +116,14 @@ class UsawaContext:
def load_accounts(self):
+ if self.uidx == None:
+ logg.info('no unit index, skip accounts')
+ return
s = self.cfg.get('ACCOUNTS_FILE')
if s:
self.aidx = AccountIndex.from_file(self.uidx, s)
else:
- self.aidx = AccountIndex(uidx)
+ self.aidx = AccountIndex(self.uidx)
if self.cfg.true('ACCOUNTS_STRICT'):
self.aidx.lock()
@@ -145,8 +148,11 @@ class UsawaContext:
self.store = LedgerStore(self.db, self.ledger)
self.keystore = self.store
elif store_scope == 'asset' or store_score == 'entry':
- self.store = EntryStore(self.db)
self.keystore = KeyStore(self.db)
+ if store_scope == 'asset':
+ self.store = AssetStore(self.db)
+ else:
+ self.store = EntryStore(self.db)
def create_resolver(self, resolver_type='fs'):
@@ -158,6 +164,3 @@ class UsawaContext:
else:
logg.debug('missing resolver')
logg.info('created {}'.format(self.resolver))
-
-
-
diff --git a/dummy/usawa/data/usawa.ini b/dummy/usawa/data/usawa.ini
@@ -1,6 +1,7 @@
[main]
gpg_dir =
ledger_file =
+timezone = UTC
[accounts]
file =
diff --git a/dummy/usawa/entry.py b/dummy/usawa/entry.py
@@ -221,18 +221,19 @@ class Entry(UsawaElement):
return Entry(serial, tx_date, ref=ref, description=description, parent=parent, tx_datereg=tx_datereg, unitindex=unitindex, tags=tags)
- def clone(self, unitindex=None, description=None, invert_parts=False):
+ def clone(self, unitindex=None, description=None, invert_parts=False, include_parts=True):
if description == None:
description = self.description + ' (clone)'
o = Entry(-1, self.dt, ref=self.ref, description=self.description, unitindex=unitindex, tags=self.tags)
- for v in self.debit:
- if invert_parts:
- v.amount *= -1
- o.add_part(v)
- for v in self.credit:
- if invert_parts:
- v.amount *= -1
- o.add_part(v)
+ if include_parts:
+ for v in self.debit:
+ if invert_parts:
+ v.amount *= -1
+ o.add_part(v)
+ for v in self.credit:
+ if invert_parts:
+ v.amount *= -1
+ o.add_part(v)
return o
@@ -680,6 +681,21 @@ class Entry(UsawaElement):
return b.encode('utf-8')
+
+ def tag(self, tag):
+ if self.tags == None:
+ self.tags = []
+ elif not tag in self.tags:
+ self.tags.append(tag)
+
+
+ def untag(self, tag):
+ try:
+ self.tags.remove(tag)
+ except ValueError:
+ pass
+
+
def __iter__(self):
self.parts = self.debit + self.credit
return self
diff --git a/dummy/usawa/runnable/asset.py b/dummy/usawa/runnable/asset.py
@@ -6,40 +6,13 @@ from whee.valkey import ValkeyStore
import usawa.config
from usawa import Asset
+from usawa.context import UsawaContext
from usawa.store import AssetStore
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
-class Context:
-
- def __init__(self, args):
- self.cfg = usawa.config.load_config(config_dir=args.c)
- self.state = 0
- self.asset = None
-
- if self.cfg.get('STORE_TYPE') == 'valkey':
- dbid = self.cfg.get('VALKEY_ID')
- host = self.cfg.get('VALKEY_HOST')
- port = self.cfg.get('VALKEY_PORT')
- self.db = ValkeyStore('', host=host, port=port)
- elif self.cfg.get('STORE_TYPE') == 'fs':
- base = self.cfg.get('FSSTORE_BASE')
- self.db = FsStore(base=base, dbname='usawa')
- self.store = LedgerStore(self.db, self.ledger)
-
- if args.f:
- self.asset = Asset.from_file(args.f, extref=args.e, mimetype=args.m, slug=args.n)
- elif args.z:
- digest = bytes.fromhex(args.z)
- self.asset = Asset(digest=digest, ref=args.e, mimetype=args.m, slug=args.n)
- else:
- raise ValueError('Must provide either file path or digest')
-
- self.store.add_asset(self.asset, overwrite=True)
-
-
argp = argparse.ArgumentParser()
argp.add_argument('-e', type=str, help='unique reference of asset')
argp.add_argument('-z', type=str, help='digest of asset')
@@ -54,6 +27,19 @@ args = argp.parse_args()
if args.v:
logg.setLevel(getattr(logging, args.v.upper()))
-ctx = Context(args)
+cfg = usawa.config.load_config(config_dir=args.c)
+ctx = UsawaContext(cfg, signing=False)
+ctx.init(args, store_scope='asset')
+
+asset = None
+if args.f:
+ asset = Asset.from_file(args.f, extref=args.e, mimetype=args.m, slug=args.n)
+elif args.z:
+ digest = bytes.fromhex(args.z)
+ asset = Asset(digest=digest, ref=args.e, mimetype=args.m, slug=args.n)
+else:
+ raise ValueError('Must provide either file path or digest')
+
+ctx.store.add_asset(asset, overwrite=True)
-print(ctx.asset)
+print(asset.ref)
diff --git a/dummy/usawa/runnable/entry.py b/dummy/usawa/runnable/entry.py
@@ -12,6 +12,7 @@ from whee.valkey import ValkeyStore
from whee.fs import FsStore
import usawa.config
+from usawa.context import UsawaContext
from usawa import Entry, Ledger, EntryPart, Asset, DemoWallet
from usawa.store import LedgerStore
from usawa.resolve.fs import FSResolver
@@ -22,144 +23,226 @@ logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
-
-class Context:
-
- def __init__(self, args):
- self.cfg = usawa.config.load_config(config_dir=args.c)
- self.state = 0
- self.commit = args.commit
-
- # entry parts
- self.description = None
- self.src = []
- self.dst = []
- self.amount = None
- self.output = None
- self.f = None
- if args.t:
- self.txdate = datetime.datetime.fromisoformat(args.t)
+def try_entry_uuid(ctx, v):
+ v = uuid.UUID(v)
+ entry = Entry.empty(ref=str(v))
+ return ctx.store.get_draft(entry)
+
+
+def try_entry_serial(ctx, v):
+ v = int(v)
+ entry = Entry(v, None)
+ return ctx.store.get_entry(entry)
+
+
+def try_entry_digest(ctx, k):
+ if isinstance(k, str):
+ k = bytes.fromhex(k)
+ if len(k) != 64:
+ raise ValueError('invalid digest length')
+ v = ctx.resolver.get(k)
+ return Entry.from_string(v, ctx.uidx)
+
+
+def try_entry(ctx, args):
+ if not args.entry:
+ return Entry.empty(unitindex=ctx.uidx)
+
+ try:
+ return try_entry_uuid(ctx, args.entry)
+ except ValueError:
+ pass
+
+ try:
+ return try_entry_serial(ctx, args.entry)
+ except ValueError:
+ pass
+
+ try:
+ return try_entry_digest(ctx, args.entry)
+ except ValueError:
+ pass
+
+ return None
+
+
+#class Context:
+#
+# def __init__(self, args):
+# self.cfg = usawa.config.load_config(config_dir=args.c)
+# self.state = 0
+# self.commit = args.commit
+#
+# # entry parts
+# self.description = None
+# self.src = []
+# self.dst = []
+# self.amount = None
+# self.output = None
+# self.f = None
+# if args.t:
+# self.txdate = datetime.datetime.fromisoformat(args.t)
+# else:
+# self.txdate = datetime.datetime.now(datetime.UTC)
+# self.attach = []
+#
+# # set up ledger
+# s = args.ledger_file
+# if not s:
+# try:
+# s = self.cfg.get('MAIN_LEDGER_FILE')
+# except KeyError:
+# pass
+# if not s:
+# raise ValueError('ledger file required')
+# self.ledger = Ledger.from_file(s)
+# self.fp = args.o
+# self.fp_bak = None
+# if self.fp == None:
+# self.fp = s
+# self.fp_bak = s + '_' + str(self.ledger.serial)
+# self.uidx = self.ledger.uidx
+#
+# # set up accounts hierarchy, if applicable
+# self.accounts = None
+# s = self.cfg.get('ACCOUNTS_FILE')
+# if s:
+# self.accounts = AccountIndex.from_file(self.uidx, s)
+# else:
+# self.accounts = AccountIndex(self.uidx)
+# if self.cfg.true('ACCOUNTS_STRICT'):
+# self.accounts.lock()
+#
+# self.entry = Entry(-1, self.txdate, ref=args.e)
+# self.db = None
+# if self.cfg.get('STORE_TYPE') == 'valkey':
+# dbid = self.cfg.get('VALKEY_ID')
+# host = self.cfg.get('VALKEY_HOST')
+# port = self.cfg.get('VALKEY_PORT')
+# self.db = ValkeyStore('', host=host, port=port)
+# elif self.cfg.get('STORE_TYPE') == 'fs':
+# base = self.cfg.get('FSSTORE_BASE')
+# self.db = FsStore(base=base, dbname='usawa')
+# self.store = LedgerStore(self.db, self.ledger)
+# ops = int(self.cfg.get('WALLET_OPSLIMIT', 0))
+# mem = int(self.cfg.get('WALLET_MEMLIMIT', 0))
+# pw = self.cfg.get('WALLET_KEY_PASSPHRASE')
+# if args.p and pw == None:
+# pw = getpass.getpass("passphrase: ")
+# self.wallet = self.store.get_key(DemoWallet, passphrase=pw, opslimit=ops, memlimit=mem)
+# self.ledger.set_wallet(self.wallet)
+# if args.e:
+# self.entry = self.store.get_draft(self.entry)
+# self.state = 1
+# else:
+# self.store.put_draft(self.entry)
+# self.ref = self.entry.get_ref()
+#
+# self.base = self.uidx.base
+# self.k = 'src'
+# self.havedst = False
+# self.i = 0
+# self.resolver = None
+# resolver_path = self.cfg.get('FS_RESOLVER_STORE_PATH', args.r)
+# if resolver_path != None:
+# self.resolver = FSResolver(os.path.realpath(resolver_path))
+#
+
+def parse_type(ctx, v):
+ v = v.lower()
+ r = None
+ for k in CATEGORIES:
+ if k.startswith(v):
+ r = k
+ logg.info("expanded input '{}' to category {}".format(v, r))
+ break
+ if not r:
+ raise ValueError('invalid type: ' + v)
+ o = getattr(AccountType, r)
+ logg.debug('accounttype {}'.format(o))
+ return o
+
+
+def parse_unit(ctx, v):
+ return ctx.uidx.sym(v)
+
+
+#def parse_account(ctx, v, typ, sym):
+# account = self.accounts.check(sym, typ, v)
+# if account:
+# account = Account.from_path(account)
+# else:
+# account = self.accounts.add(v, sym=sym, typ=typ)
+# return account
+
+
+def parse_amount(ctx, sym, v):
+ return ctx.uidx.from_floatstring(sym, v)
+
+
+def parse_side(ctx, v):
+ for k in ['src', 'dst']:
+ if k.startswith(v):
+ return k
+ raise ValueError('invalid side: ' + v)
+
+
+def choose_account(ctx, include_create=False):
+ r = None
+ accounts_r = []
+ while not r:
+ a = None
+ if len(accounts_r) == 0:
+ a = input('enter account: ')
+ if a == 'q':
+ raise AbortMenu()
+ elif len(accounts_r) == 1:
+ r = accounts_r[0]
+ break
else:
- self.txdate = datetime.datetime.now(datetime.UTC)
- self.attach = []
-
- # set up ledger
- s = args.ledger_file
- if not s:
+ v = input('choose match: ')
+ i = None
+ if v == 'q':
+ raise AbortMenu()
try:
- s = self.cfg.get('MAIN_LEDGER_FILE')
- except KeyError:
- pass
- if not s:
- raise ValueError('ledger file required')
- self.ledger = Ledger.from_file(s)
- self.fp = args.o
- self.fp_bak = None
- if self.fp == None:
- self.fp = s
- self.fp_bak = s + '_' + str(self.ledger.serial)
- self.uidx = self.ledger.uidx
-
- # set up accounts hierarchy, if applicable
- self.accounts = None
- s = self.cfg.get('ACCOUNTS_FILE')
- if s:
- self.accounts = AccountIndex.from_file(self.uidx, s)
- else:
- self.accounts = AccountIndex(self.uidx)
- if self.cfg.true('ACCOUNTS_STRICT'):
- self.accounts.lock()
-
- self.entry = Entry(-1, self.txdate, ref=args.e)
- self.db = None
- if self.cfg.get('STORE_TYPE') == 'valkey':
- dbid = self.cfg.get('VALKEY_ID')
- host = self.cfg.get('VALKEY_HOST')
- port = self.cfg.get('VALKEY_PORT')
- self.db = ValkeyStore('', host=host, port=port)
- elif self.cfg.get('STORE_TYPE') == 'fs':
- base = self.cfg.get('FSSTORE_BASE')
- self.db = FsStore(base=base, dbname='usawa')
- self.store = LedgerStore(self.db, self.ledger)
- ops = int(self.cfg.get('WALLET_OPSLIMIT', 0))
- mem = int(self.cfg.get('WALLET_MEMLIMIT', 0))
- pw = self.cfg.get('WALLET_KEY_PASSPHRASE')
- if args.p and pw == None:
- pw = getpass.getpass("passphrase: ")
- self.wallet = self.store.get_key(DemoWallet, passphrase=pw, opslimit=ops, memlimit=mem)
- self.ledger.set_wallet(self.wallet)
- if args.e:
- self.entry = self.store.get_draft(self.entry)
- self.state = 1
- else:
- self.store.put_draft(self.entry)
- self.ref = self.entry.get_ref()
-
- self.base = self.uidx.base
- self.k = 'src'
- self.havedst = False
- self.i = 0
- self.resolver = None
- resolver_path = self.cfg.get('FS_RESOLVER_STORE_PATH', args.r)
- if resolver_path != None:
- self.resolver = FSResolver(os.path.realpath(resolver_path))
-
-
- def parse_type(self, v):
- v = v.lower()
- r = None
- for k in CATEGORIES:
- if k.startswith(v):
- r = k
- logg.info("expanded input '{}' to category {}".format(v, r))
- break
- if not r:
- raise ValueError('invalid type: ' + v)
- o = getattr(AccountType, r)
- logg.debug('accounttype {}'.format(o))
- return o
-
-
- def parse_unit(self, v):
- return self.uidx.sym(v)
-
-
- def parse_account(self, v, typ, sym):
- account = self.accounts.check(sym, typ, v)
- if account:
- account = Account.from_path(account)
- else:
- account = self.accounts.add(v, sym=sym, typ=typ)
- return account
-
-
- def parse_amount(self, uidx, sym, v):
- return uidx.from_floatstring(sym, v)
-
-
- def parse_side(self, v):
- for k in ['src', 'dst']:
- if k.startswith(v):
- return k
- raise ValueError('invalid side: ' + v)
-
-
- def add_part(self, part):
- logg.info('add part {}'.format(part))
- self.part.append(part)
-
-
- def validate(self):
- if len(self.src) == 0:
- raise ValueError('no src')
- if len(self.dst) == 0:
- raise ValueError('no dst')
- if self.ref == None:
- raise ValueError('invalid ref')
-
-
- def parse_txdate(self, v):
- return datetime.date.fromisoformat(v)
+ i = int(v)
+ except ValueError:
+ logg.debug('Invalid number, going back to search')
+ if i != None:
+ try:
+ r = accounts_r[i]
+ except IndexError:
+ logg.error('Number out of range')
+ continue
+ accounts_r = []
+ ctx.aidx.set_filter(path=a)
+ i = 0
+ for a in ctx.aidx:
+ accounts_r.append(a)
+ print('{} {}'.format(i, a))
+ i += 1
+ if i == 0:
+ logg.debug('no accounts found')
+ ctx.aidx.reset_filter()
+ return r
+
+
+def add_part(self, part):
+ logg.info('add part {}'.format(part))
+ self.part.append(part)
+
+
+def validate(self):
+ if len(self.src) == 0:
+ raise ValueError('no src')
+ if len(self.dst) == 0:
+ raise ValueError('no dst')
+ if self.ref == None:
+ raise ValueError('invalid ref')
+
+
+def parse_txdate(ctx, v):
+ return datetime.date.fromisoformat(v)
def input_or_default(prompt, default=None, postfix=': ', validate_fn=None):
@@ -176,60 +259,104 @@ def input_or_default(prompt, default=None, postfix=': ', validate_fn=None):
def do_interactive_one(ctx):
- ctx.description = input_or_default('Entry description', ctx.description)
- ctx.ref = input_or_default('External ref', ctx.ref)
- dt = datetime.datetime.utcnow()
- dt = input_or_default('Transaction date(time)', ctx.txdate)
- if isinstance(dt, str):
- dt = ctx.parse_txdate(dt)
- ctx.txdate = dt
+ ctx.set('havesrc', False)
+ ctx.set('havedst', False)
+ ctx.set('partk', 'src')
+ v = input_or_default('Entry description', ctx.get('description'))
+ ctx.set('description', v)
+ v = input_or_default('External ref', ctx.get('ref'))
+ ctx.set('ref', v)
+ v = input_or_default('Transaction date(time)', ctx.get('dt'))
+ if isinstance(v, str):
+ v = parse_txdate(ctx, v)
+ ctx.set('dt', v)
+
+
+def handle_tag(ctx, entry, v):
+ r = input('Tag: ')
+ if v == '+':
+ entry.tag(r)
+ elif v == '-':
+ entry.untag(r)
+
+
+def handle_input(ctx, entry, v):
+ if v == 'i' or v == 'o':
+ k = 'src'
+ if v == 'o':
+ k = 'dst'
+ enter_part(ctx, entry, side=k)
+ return True
+ if v == 'q':
+ raise StopIteration()
+ if v == '+' or v == '-':
+ r = handle_tag(ctx, entry, v)
+ return True
+ if v == 'w':
+ ctx.set('commit', True)
+ return False
+ if v == 't':
+ return False
+ logg.error('invalid input')
+ return True
def do_interactive_two(ctx, entry):
r = True
while r:
- try:
- r = enter_part(ctx, entry)
- except Exception as e:
- logg.error('err {}'.format(e))
- pass
-
-
-def enter_part(ctx, entry):
- if ctx.i > 0:
- if ctx.havedst:
- ctx.k = ''
- else:
- ctx.k = 'dst'
- v = input_or_default('Entry side', ctx.k)
- if v == '':
- return False
- ctx.k = ctx.parse_side(v)
- if v == 'dst':
- ctx.havedst = True
-
- v = input_or_default('Entry {} unit'.format(ctx.k), ctx.base)
- unit = ctx.parse_unit(v)
- if ctx.base != unit:
- ctx.base = unit
-
- v = input_or_default('Entry {} type'.format(ctx.k))
- typ = ctx.parse_type(v)
-
- v = input_or_default('Entry {} account'.format(ctx.k))
- account = ctx.parse_account(v, sym=unit, typ=typ)
-
- amount = None
- v = input_or_default('Entry {} amount'.format(ctx.k), amount)
- amount = ctx.parse_amount(ctx.uidx, unit, v)
-
- isdebit = ctx.k=='src'
- part = EntryPart(unit, typ.value, account.to_path(display=AccountDisplay.path), amount, debit=isdebit)
- entry.add_part(part)
-
- ctx.i += 1
+# try:
+# r = enter_part(ctx, entry)
+# except Exception as e:
+# logg.error('err {}'.format(e))
+# pass
+ v = input("> ")
+ r = handle_input(ctx, entry, v)
+
+
+def enter_part(ctx, entry, side=None):
+ if side != None:
+ ctx.set('partk', side)
+ elif len(entry.debit) > 0:
+ if ctx.get('havedst'):
+ ctx.set('partk', 'dst')
+ else:
+ ctx.set('partk', 'src')
+ v = input_or_default('Entry side', ctx.get('partk'))
+ if v == '':
+ return False
+ k = parse_side(ctx, v)
+ ctx.set('partk', k)
+
+ #v = input_or_default('Entry {} account'.format(ctx.get('partk')))
+ #account = parse_account(ctx, v, sym=unit, typ=typ)
+ v = choose_account(ctx)
+ account = Account.from_path(v)
+
+ #amount = None
+ k = ctx.get('partk')
+ v = input_or_default('Account {} amount'.format(account))
+ amount = parse_amount(ctx, account.sym, v)
+
+ isdebit = k=='src'
+ part = EntryPart(account.sym, account.typ.value.lower(), account.to_path(display=AccountDisplay.path), amount, debit=isdebit)
+ logg.debug('addpart {}'.format(part))
+ entry.add_part(part)
+
+ if ctx.get('partk') == 'src':
+ ctx.set('havesrc', True)
+
+ if ctx.get('partk') == 'dst':
+ ctx.set('havedst', True)
+
+ return True
+
+
+def do_prepare(ctx, entry=None):
+ uu = uuid.uuid4()
+ ctx.set('ref', str(uu))
+ dt = datetime.datetime.now(datetime.UTC)
+ ctx.set('dt', dt)
- return True
def main():
def croak(*args, **kwargs):
@@ -239,35 +366,40 @@ def main():
signal.signal(signal.SIGTERM, croak)
argp = argparse.ArgumentParser()
- argp.add_argument('-e', type=str, help='unique reference of entry')
argp.add_argument('-x', type=str, action='append', default=[], help='unique reference of attachment')
argp.add_argument('-z', type=str, action='append', default=[], help='sum of attachment')
argp.add_argument('-v', type=str, choices=['info','debug','warning','error'], help='be verbose')
argp.add_argument('-c', type=str, help='override config dir')
argp.add_argument('-t', type=str, help='transaction date or datetime')
- argp.add_argument('-r', type=str, help='export resolver spec')
+ #argp.add_argument('-r', type=str, help='export resolver spec')
+ argp.add_argument('-i', type=str, help='input ledger state')
argp.add_argument('-o', type=str, help='output ledger state')
argp.add_argument('-p', action='store_true', help='unlock wallet with password')
argp.add_argument('--commit', action='store_true', dest='commit', help='commit to ledger')
- argp.add_argument('ledger_file', type=str, help='ledger file')
+ argp.add_argument('entry', nargs='?', type=str, help='entry to edit')
args = argp.parse_args()
if args.v:
logg.setLevel(getattr(logging, args.v.upper()))
- ctx = Context(args)
-
- entry = None
- if ctx.state == 0:
- do_interactive_one(ctx)
- entry = Entry.empty(ref=ctx.ref, unitindex=ctx.uidx, tx_date=ctx.txdate)
- entry = ctx.store.get_draft(entry)
- entry.description = ctx.description
- entry.dt = ctx.txdate
- do_interactive_two(ctx, entry)
- else:
- entry = ctx.entry
-
+ #ctx = Context(args)
+
+ cfg = usawa.config.load_config(config_dir=args.c)
+ ctx = UsawaContext(cfg)
+ #dp = os.path.realpath(args.src_dir)
+ #ctx.set('srcdir', dp)
+ ctx.init(args)
+ ctx.set('unitbase', ctx.uidx.base)
+ ctx.set('commit', False)
+
+ entry = try_entry(ctx, args)
+ if entry.serial > 0:
+ raise NotImplementedError('entry edit not yet implemented')
+ do_prepare(ctx, entry=entry)
+# entry = Entry.empty(ref=ctx.get('ref'), unitindex=ctx.uidx, tx_date=ctx.get('dt'))
+ #entry = ctx.store.get_draft(entry)
+ #entry.description = ctx.get('description')
+ #entry.dt = ctx.txdate
for v in args.x:
k = uuid.UUID(v)
@@ -280,23 +412,28 @@ def main():
asset = ctx.store.get_asset(asset)
entry.attach(asset)
- if ctx.commit:
- if entry.serial != -1:
- raise AttributeError('entry draft already marked as committed')
- entry.serial = ctx.ledger.next_serial()
+ do_interactive_one(ctx)
+ try:
+ do_interactive_two(ctx, entry)
+ except StopIteration:
+ sys.exit(0)
+# if ctx.commit:
+# if entry.serial != -1:
+# raise AttributeError('entry draft already marked as committed')
+# entry.serial = ctx.ledger.next_serial()
- v = input_or_default('Commit? (type YES, any other input is no)', '')
- if v == 'YES':
+# v = input_or_default('Commit? (type YES, any other input is no)', '')
+ if ctx.get('commit'):
entry.parent = ctx.ledger.cur
entry.serial = ctx.ledger.next_serial()
entry.sign(ctx.wallet)
ctx.store.add_entry(entry, update_ledger=True)
ctx.ledger.truncate()
ctx.ledger.sign()
- if ctx.fp_bak != None:
- shutil.copy(ctx.fp, ctx.fp_bak)
- f = open(ctx.fp, 'w')
+# if ctx.fp_bak != None:
+# shutil.copy(ctx.fp, ctx.fp_bak)
+ f = open(ctx.ledger_path_out, 'w')
f.write(ctx.ledger.to_string())
f.close()
if ctx.resolver != None:
diff --git a/dummy/usawa/unit.py b/dummy/usawa/unit.py
@@ -112,6 +112,7 @@ class UnitIndex:
precision = int(o.find('precision', namespaces=nsmap()).text)
logg.debug('add unit {} precision {}'.format(o.get('sym'), precision))
r.detail[o.get('sym')] = precision
+ r.rate[o.get('sym')] = 1000000000
r.check()
return r
@@ -230,9 +231,10 @@ class UnitIndex:
if l < c:
r += '0' * (c - l)
r = s[0] + r
+ r = int(r)
if neg:
r *= -1
- return int(r)
+ return r
def from_float(self, sym, v):