commit 20303dfe4869b48a6f21d97db044ce05f81d3588
parent fe1638302dd048dd8be32f729ccbed9b5190ebd3
Author: lash <dev@holbrook.no>
Date: Sun, 22 Mar 2026 16:58:42 -0600
Add base element with tags and key-value metadata
Diffstat:
10 files changed, 58 insertions(+), 36 deletions(-)
diff --git a/dummy/usawa/asset.py b/dummy/usawa/asset.py
@@ -2,12 +2,12 @@ import hashlib
import mimetypes
import logging
import os
-import uuid
import lxml.etree
import rencode
import magic
+from .base import UsawaElement
from .constant import NSPREFIX
from .xml import nsmap
@@ -46,7 +46,7 @@ def parse_path(path):
)
-class Asset:
+class Asset(UsawaElement):
"""Represents a file asset, used as attachment in usawa.Entry.
Object is not intended to be instantiated directly. Instead one of the following static methods should be used:
@@ -56,14 +56,14 @@ class Asset:
Asset.from_tree() - Recreate from an XML tree.
"""
- def __init__(self, digest=None):
+ def __init__(self, digest=None, ref=None):
+ super(Asset, self).__init__(ref=ref)
self.digest = digest
self.mime = None
self.enc = None
self.slug = None
self.ext = None
self.extref = None
- self.uuid = None
self.description = None
"""Return the preferred filename with extension for the asset.
@@ -202,7 +202,6 @@ class Asset:
h = hashlib.sha1()
h.update(o.digest)
- # o.uuid = str(uuid.uuid1(h.digest()))
o.extref = extref
o.description = description
@@ -244,8 +243,7 @@ class Asset:
if canon:
return tree
- # if self.uuid != None:
- # tree.set('uuid', self.uuid)
+ tree.set('ref', self.ref)
if self.extref != None:
o = lxml.etree.SubElement(tree, "extref")
@@ -278,8 +276,8 @@ class Asset:
@staticmethod
def from_tree(tree):
- o = Asset()
- # o.uuid = tree.get('uuid')
+ uuid = tree.get('ref')
+ o = Asset(ref=uuid)
o.mime = tree.get("mime")
v = tree.find("digest", namespaces=nsmap()).text
o.digest = bytes.fromhex(v)
@@ -312,8 +310,8 @@ class Asset:
def to_list(self):
d = []
- # for k in ['mime', 'uuid', 'slug', 'ext', 'description', 'extref', 'enc']:
- for k in ["mime", "slug", "ext", "description", "extref", "enc"]:
+ for k in ['mime', 'ref', 'slug', 'ext', 'description', 'extref', 'enc']:
+ #for k in ["mime", "slug", "ext", "description", "extref", "enc"]:
v = getattr(self, k)
d.append(v)
return d
@@ -341,8 +339,8 @@ class Asset:
o = Asset()
v = rencode.loads(data)
i = 0
- # for k in ['mime', 'uuid', 'slug', 'ext', 'description', 'extref', 'enc']:
- for k in ["mime", "slug", "ext", "description", "extref", "enc"]:
+ for k in ['mime', 'ref', 'slug', 'ext', 'description', 'extref', 'enc']:
+ #for k in ["mime", "slug", "ext", "description", "extref", "enc"]:
vv = v[i]
if isinstance(vv, bytes):
vv = vv.decode("utf-8")
diff --git a/dummy/usawa/data/schema.xsd b/dummy/usawa/data/schema.xsd
@@ -130,7 +130,7 @@
<xs:element name="data" type="EntryData" />
<xs:element name="sig" type="Signature" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
- <xs:attribute name="uuid" type="xs:string" />
+ <xs:attribute name="ref" type="xs:string" />
</xs:complexType>
<xs:complexType name="ParentRef">
@@ -173,7 +173,7 @@
<xs:element name="sig" type="Signature" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="mime" type="xs:string" />
- <xs:attribute name="uuid" type="xs:string" />
+ <xs:attribute name="ref" type="xs:string" />
</xs:complexType>
<xs:simpleType name="accountType" final="restriction">
diff --git a/dummy/usawa/entry.py b/dummy/usawa/entry.py
@@ -1,12 +1,12 @@
import enum
import logging
import datetime
-import uuid
import hashlib
import lxml.etree
import rencode
+from .base import UsawaElement
from .constant import DEFAULTPARENT, NSPREFIX
from .crypto import DemoWallet
from .error import ACLError, VerifyError
@@ -150,7 +150,7 @@ class EntryPart:
return '[{}] {}:{} {}'.format(pfx, self.typ, self.account, self.amount)
-class Entry:
+class Entry(UsawaElement):
digest_algo = 'sha512' # The algorithm used for generating entry digests. Must match a hashlib (standard library) type string identifier.
@@ -181,15 +181,13 @@ class Entry:
:todo: Ensure canonical format of keyid.
"""
def __init__(self, serial, tx_date, ref=None, description=None, parent=None, tx_datereg=None, unitindex=None):
+ super(Entry, self).__init__(ref=ref)
if isinstance(parent, str):
parent = bytes.fromhex(parent)
elif parent == None:
parent = DEFAULTPARENT
elif len(parent) != 64:
raise ValueError('invalid parent hash')
- if ref == None:
- ref = str(uuid.uuid4())
- self.ref = ref
self.parent = parent
self.serial = serial
self.dt = tx_date
@@ -207,6 +205,14 @@ class Entry:
self.lookup = None
self.lookup_algo = None
+ """Get the unique non-sum identifier of the entry as a string value.
+
+ :returns: ref
+ :rtype: str
+ """
+ def get_ref(self):
+ return self.ref
+
"""Add an entry part to the entry.
@@ -328,8 +334,10 @@ class Entry:
for v in self.attachment:
attach.append(v.get_digest(binary=True))
+ base = super(Entry, self).serialize()
logg.debug('serializing with parent {}'.format(self.parent.hex()))
d = [
+ base,
self.parent,
self.serial,
self.ref,
@@ -364,17 +372,19 @@ class Entry:
def deserialize(data):
v = rencode.loads(data)
#parent = v[0].hex()
- parent = v[0]
- serial = v[1]
- ref = v[2].decode('utf-8')
- date_reg = datetime.datetime.strptime(v[3].decode('utf-8'), '%Y%m%d%H%M%S')
- date = datetime.datetime.strptime(v[4].decode('utf-8'), '%Y%m%d')
+ parent = v[1]
+ serial = v[2]
+ ref = v[3].decode('utf-8')
+ date_reg = datetime.datetime.strptime(v[4].decode('utf-8'), '%Y%m%d%H%M%S')
+ date = datetime.datetime.strptime(v[5].decode('utf-8'), '%Y%m%d')
#unit = v[5].decode('utf-8')
- description = v[5].decode('utf-8')
- dst_data = v[6]
- src_data = v[7]
- attach_data = v[8]
+ description = v[6].decode('utf-8')
+ dst_data = v[7]
+ src_data = v[8]
+ attach_data = v[9]
o = Entry(serial, date, ref=ref, description=description, parent=parent, tx_datereg=date_reg)
+ logg.debug('tup {}'.format(v[0]))
+ super(Entry, o).deserialize(v[0])
for v in src_data:
#src = EntryPart(v[0].decode('utf-8'), v[1].decode('utf-8'), v[2].decode('utf-8'), v[3], debit=True)
src = EntryPart.deserialize(v, debit=True)
diff --git a/dummy/usawa/gui/__init__.py b/dummy/usawa/gui/__init__.py
@@ -21,11 +21,12 @@ class Usawa(Adw.Application):
self.win = None
self.ledger_file = None
self.cfg = None
+ self.account_list = None
self.connect("activate", self.on_activate)
signal.signal(signal.SIGINT, self._handle_sigint)
def on_activate(self, app):
- self.win = UsawaMainWindow(application=app, ledger_path=self.ledger_file)
+ self.win = UsawaMainWindow(application=app, ledger_path=self.ledger_file, account_list=self.account_list)
self.win.present()
def _handle_sigint(self, *_):
diff --git a/dummy/usawa/gui/main_window.py b/dummy/usawa/gui/main_window.py
@@ -24,8 +24,11 @@ logg = logging.getLogger("gui.mainwindow")
class UsawaMainWindow(Adw.ApplicationWindow):
- def __init__(self, application, ledger_path=None, **kwargs):
+ def __init__(self, application, ledger_path=None, account_list=None, **kwargs):
super().__init__(application=application, **kwargs)
+ logg.debug('foo')
+
+ self.account_list = account_list
self.set_title("Usawa")
self.set_default_size(1000, 600)
@@ -118,6 +121,7 @@ class UsawaMainWindow(Adw.ApplicationWindow):
entries=entries,
refresh_callback=self.refresh_entries,
toast_overlay=self.toast_overlay,
+ account_list=self.account_list,
)
self.entry_list_view._load_entries()
page.set_child(self.entry_list_view)
diff --git a/dummy/usawa/gui/views/create_entry_view.py b/dummy/usawa/gui/views/create_entry_view.py
@@ -6,11 +6,11 @@ import mimetypes
logg = logging.getLogger("gui.create_entry_view")
-def create_entry_page(nav_view, controller):
+def create_entry_page(nav_view, controller, account_list=None):
"""Create a new entry page"""
page = Adw.NavigationPage(title="Create New Entry", tag="create-entry")
- view = CreateEntryView(nav_view, controller)
+ view = CreateEntryView(nav_view, controller, account_list=account_list)
page.set_child(view)
return page
@@ -19,11 +19,12 @@ def create_entry_page(nav_view, controller):
class CreateEntryView(Gtk.Box):
"""Create entry view - UI ONLY"""
- def __init__(self, nav_view, controller):
+ def __init__(self, nav_view, controller, account_list=None):
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=0)
self.nav_view = nav_view
self.controller = controller
+ self.account_list = account_list
self.attachment_paths: list[str] = []
self._build_ui(nav_view)
diff --git a/dummy/usawa/gui/views/entry_list_view.py b/dummy/usawa/gui/views/entry_list_view.py
@@ -19,6 +19,7 @@ class EntryListView(Gtk.Box):
toast_overlay,
entries=None,
refresh_callback=None,
+ account_list=None,
**kwargs,
):
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=0, **kwargs)
@@ -28,6 +29,7 @@ class EntryListView(Gtk.Box):
self.entries = entries or []
self.refresh_callback = refresh_callback
self.toast_overlay = toast_overlay
+ self.account_list = account_list
self.active_filter = None
overlay = Gtk.Overlay()
@@ -319,7 +321,7 @@ class EntryListView(Gtk.Box):
def on_fab_clicked(self, button):
logg.info("FAB clicked - opening create entry window")
- create_page = create_entry_page(self.nav_view, self.entry_controller)
+ create_page = create_entry_page(self.nav_view, self.entry_controller, account_list=self.account_list)
self.nav_view.push(create_page)
def refresh_data(self):
diff --git a/dummy/usawa/ledger.py b/dummy/usawa/ledger.py
@@ -9,6 +9,7 @@ import lxml.etree
import rencode
import varints.leb128s
+from .base import UsawaElement
from .crypto import DemoWallet, ACL
from .xml import nsmap, XML_FORMAT_VERSION
from .constant import NSPREFIX, DEFAULTPARENT
@@ -211,7 +212,7 @@ class RunningTotal:
return 'running total {}: income {} expense {} asset {} liability {}'.format(self.sym, self.income, self.expense, self.asset, self.liability)
-class Ledger:
+class Ledger(UsawaElement):
default_src = 'defalsify.org'
diff --git a/dummy/usawa/runnable/gui.py b/dummy/usawa/runnable/gui.py
@@ -9,3 +9,7 @@ logging.basicConfig(level=logging.DEBUG)
def main():
app = Usawa(application_id='org.usawa.app')
app.run(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/dummy/usawa/store.py b/dummy/usawa/store.py
@@ -16,6 +16,7 @@ PFX_LEDGER_LOCK = b'\x03'
PFX_ENTRY = b'\x04'
PFX_UNIT_INDEX = b'\x08'
PFX_ASSET = b'\x10'
+PFX_AUX = b'\xfe'
logg = logging.getLogger('usawa.store')