usawa

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

commit 5358ab86bb328b0912e4e468fe93ce887f0c1bd1
parent beabc49b77a26cfaf8263d34392d02ff44f9605c
Author: lash <dev@holbrook.no>
Date:   Sun, 22 Mar 2026 22:29:43 -0600

Add asset adder cli

Diffstat:
Mdummy/tests/store.py | 1+
Mdummy/usawa/asset.py | 17++++++++++++-----
Adummy/usawa/runnable/asset.py | 47+++++++++++++++++++++++++++++++++++++++++++++++
Mdummy/usawa/runnable/craft.py | 27++++++++++++++++++++-------
4 files changed, 80 insertions(+), 12 deletions(-)

diff --git a/dummy/tests/store.py b/dummy/tests/store.py @@ -193,5 +193,6 @@ class TestStore(unittest.TestCase): self.assertEqual(o.get_digest(binary=True), hash_of_foo) self.assertEqual(o.get_ref(binary=True), uuid_for_foo.bytes) + if __name__ == '__main__': unittest.main() diff --git a/dummy/usawa/asset.py b/dummy/usawa/asset.py @@ -56,13 +56,20 @@ class Asset(UsawaElement): Asset.from_tree() - Recreate from an XML tree. """ - def __init__(self, digest=None, ref=None): + def __init__(self, digest=None, ref=None, slug=None, mimetype=None): super(Asset, self).__init__(ref=ref) self.digest = digest - self.mime = None - self.enc = None - self.slug = None self.ext = None + if mimetype == None: + mimetype = 'application/octet-stream' + else: + ext = mimetypes.guess_extension(mimetype, strict=True) + self.ext = ext[1:] + self.mime = mimetype + self.enc = None + if slug == None: + slug = self.get_ref() + self.slug = slug self.extref = None self.description = None @@ -190,10 +197,10 @@ class Asset(UsawaElement): closer() o.digest = h.digest() + (o.slug, o.ext) = parse_path(src) s = mimetypes.guess_extension(o.mime, strict=True) if s != None: o.ext = s[1:] - (o.slug, o.ext) = parse_path(src) if slug != None: logg.info("overriding file base name {} -> {}".format(o.slug, slug)) o.slug = slug diff --git a/dummy/usawa/runnable/asset.py b/dummy/usawa/runnable/asset.py @@ -0,0 +1,47 @@ +import argparse +import logging +import os + +from whee.valkey import ValkeyStore + +import usawa.config +from usawa import Asset +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 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') + + + +argp = argparse.ArgumentParser() +argp.add_argument('-e', type=str, help='unique reference of asset') +argp.add_argument('-z', type=str, help='digest of asset') +argp.add_argument('-n', type=str, help='asset filename') +argp.add_argument('-f', type=str, help='asset file') +argp.add_argument('-d', type=str, help='description of asset') +argp.add_argument('-m', type=str, help='mime type of asset') +argp.add_argument('-c', type=str, help='override config dir') +argp.add_argument('-v', type=str, choices=['info','debug','warning','error'], help='be verbose') +args = argp.parse_args() + +if args.v: + logg.setLevel(getattr(logging, args.v.upper())) + +ctx = Context(args) + +print(ctx.asset) diff --git a/dummy/usawa/runnable/craft.py b/dummy/usawa/runnable/craft.py @@ -4,8 +4,8 @@ import logging from whee.valkey import ValkeyStore import usawa.config -from usawa import Entry, Ledger, EntryPart -from usawa.store import EntryStore +from usawa import Entry, Ledger, EntryPart, Asset +from usawa.store import LedgerStore from usawa.account import Account, AccountIndex, AccountType, AccountDisplay from usawa.constant import CATEGORIES @@ -61,7 +61,7 @@ class Context: host = self.cfg.get('VALKEY_HOST') port = self.cfg.get('VALKEY_PORT') self.db = ValkeyStore('', host=host, port=port) - self.store = EntryStore(self.db) + self.store = LedgerStore(self.db, self.ledger) if args.e: self.entry = self.store.get_draft(self.entry) self.state = 1 @@ -70,6 +70,7 @@ class Context: self.ref = self.entry.get_ref() + def parse_type(self, v): v = v.lower() r = None @@ -121,8 +122,8 @@ class Context: argp = argparse.ArgumentParser() argp.add_argument('-e', type=str, help='unique reference of entry') -argp.add_argument('-x', type=str, help='unique reference of attachment') -argp.add_argument('-z', type=str, help='sum of attachment') +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('-l', type=str, help='ledger file') @@ -191,14 +192,26 @@ def do_interactive(ctx): #return ctx.open(output) entry = None -if self.cmd == 'entry': +if ctx.state == 0: do_interactive(ctx) ctx.validate() entry = Entry.empty(description=ctx.description, ref=ctx.ref, unitindex=ctx.uidx) entry.add_part(ctx.part[0], debit=True) entry.add_part(ctx.part[1]) +else: entry = ctx.entry -elif self.cmd == 'asset': + + +for v in args.x: + k = uuid.UUID(k) + asset = Asset(ref=str(k)) + attach = self.store.get_asset_indexed(asset) + entry.attach(asset) +for v in args.z: + k = bytes.fromhex(v) + asset = Asset(digest=k) + attach = self.store.get_asset_indexed(asset) + entry.attach(asset) ctx.store.put_draft(entry)