return sink
+class Account:
+ """Combine name, position in tree of own, and wealth of self + children."""
+
+ def __init__(self, parent: Optional['Account'], basename: str) -> None:
+ self.local_wealth = Wealth()
+ self.basename = basename
+ self.children: list[Account] = []
+ self.parent = parent
+ if self.parent:
+ self.parent.children += [self]
+
+ @property
+ def wealth(self) -> Wealth:
+ """Total of .local_wealth with that of .children."""
+ total = Wealth()
+ total += self.local_wealth
+ for child in self.children:
+ total += child.wealth
+ return total
+
+
class DatLine:
"""Line of .dat file parsed into comments and machine-readable data."""
dat_line.booking_line = TransferLine(self, dat_line.code)
self._transfer_lines += [dat_line.booking_line]
changes = Wealth()
+ sink_account = None
self.account_changes: dict[str, Wealth] = {}
- self.sink_account = None
for transfer_line in [tl for tl in self._transfer_lines
if not tl.errors]:
if transfer_line.account not in self.account_changes:
self.account_changes[transfer_line.account] = Wealth()
if transfer_line.amount is None:
- if self.sink_account:
+ if sink_account:
transfer_line.errors += ['too many sinks']
- self.sink_account = transfer_line.account
+ sink_account = transfer_line.account
continue
change = Wealth({transfer_line.currency: transfer_line.amount})
self.account_changes[transfer_line.account] += change
changes += change
- if self.sink_account:
- self.account_changes[self.sink_account] += changes.as_sink
+ if sink_account:
+ self.account_changes[sink_account] += changes.as_sink
elif not changes.sink_empty:
self._transfer_lines[-1].errors += ['needed sink missing']
self.id_ = id_
def do_GET(self) -> None:
# pylint: disable=invalid-name,missing-function-docstring
- if self.pagename == 'booking':
+ if self.pagename == 'balance':
+ self.send_rendered(Path('balance.tmpl'),
+ {'roots': self.server.balance_roots})
+ elif self.pagename == 'booking':
self.send_rendered(
Path('booking.tmpl'),
{'dat_lines':
"""Return only those .data_lines with .code or .comment."""
return [dl for dl in self.dat_lines if not dl.is_empty]
+ @property
+ def balance_roots(self) -> list[Account]:
+ """Return tree of calculated Accounts over all .bookings."""
+ account_names = set()
+ for booking in self.bookings:
+ for account_name in booking.account_changes:
+ account_names.add(account_name)
+ full_names_to_accounts: dict[str, Account] = {}
+ for full_name in sorted(list(account_names)):
+ step_names = full_name.split(':')
+ path = ''
+ for step_name in step_names:
+ parent_name = path[:]
+ path = ':'.join([path, step_name]) if path else step_name
+ if path not in full_names_to_accounts:
+ full_names_to_accounts[path] = Account(
+ full_names_to_accounts[parent_name] if parent_name
+ else None,
+ step_name)
+ for booking in self.bookings:
+ for account_name in booking.account_changes:
+ full_names_to_accounts[account_name].local_wealth +=\
+ booking.account_changes[account_name]
+ return [ac for ac in full_names_to_accounts.values() if not ac.parent]
+
if __name__ == "__main__":
if not LEDGER_DAT: