from os import environ
from pathlib import Path
from sys import exit as sys_exit
-from typing import Optional, Self
+from typing import Any, Optional, Self
from plomlib.web import PlomHttpHandler, PlomHttpServer, PlomQueryMap
SERVER_HOST = '127.0.0.1'
PATH_TEMPLATES = Path('templates')
+PREFIX_LEDGER = 'ledger_'
+PREFIX_EDIT = 'edit_'
+TOK_STRUCT = 'structured'
+TOK_RAW = 'raw'
+EDIT_STRUCT = f'{PREFIX_EDIT}{TOK_STRUCT}'
+EDIT_RAW = f'{PREFIX_EDIT}{TOK_RAW}'
+LEDGER_STRUCT = f'{PREFIX_LEDGER}{TOK_STRUCT}'
+LEDGER_RAW = f'{PREFIX_LEDGER}{TOK_RAW}'
+
class Wealth:
"""Collects amounts mapped to currencies."""
# pylint: disable=missing-class-docstring
mapper = PlomQueryMap
+ def _send_rendered(self, tmpl_name: str, ctx: dict[str, Any]) -> None:
+ self.send_rendered(Path(f'{tmpl_name}.tmpl'), ctx)
+
def do_POST(self) -> None:
# pylint: disable=invalid-name,missing-function-docstring
- prefix_file, prefix_ledger = 'file_', 'ledger_'
+ prefix_file = 'file_'
if (file_prefixed := self.postvars.keys_prefixed(prefix_file)):
getattr(self.server, file_prefixed[0][len(prefix_file):])()
- elif (self.pagename.startswith('edit_')
+ elif (self.pagename.startswith(PREFIX_EDIT)
and self.postvars.first('apply')):
booking = self.server.bookings[int(self.path_toks[2])]
new_lines = []
- if self.pagename == 'edit_structured':
+ if self.pagename == EDIT_STRUCT:
line_keys = self.postvars.keys_prefixed('line_')
lineno_to_inputs: dict[int, list[str]] = {}
for key in line_keys:
new_id = self.server.rewrite_booking(booking.id_, new_lines)
self.redirect(Path('/bookings').joinpath(f'{new_id}'))
return
- elif self.pagename.startswith(prefix_ledger):
+ elif self.pagename.startswith(PREFIX_LEDGER):
action, id_str, dir_ =\
- self.postvars.keys_prefixed(prefix_ledger)[0].split('_')[1:]
+ self.postvars.keys_prefixed(PREFIX_LEDGER)[0].split('_')[1:]
new_id = getattr(self.server, f'{action}_booking')(
int(id_str), dir_ == 'up' if action == 'move' else 'to_end')
self.redirect(Path(self.path).joinpath(f'#{new_id}'))
def do_GET(self) -> None:
# pylint: disable=invalid-name,missing-function-docstring
ctx = {'tainted': self.server.tainted, 'path': self.path}
- if self.pagename == 'bookings' or self.pagename.startswith('edit_'):
- id_ = int(self.path_toks[2])
- if self.pagename.startswith('edit_'):
- ctx['id'] = id_
- if self.pagename == 'balance':
- id_ = int(self.params.first('up_incl') or '-1')
- valid, balance_roots = self.server.balance_roots(id_)
- self.send_rendered(Path('balance.tmpl'),
- ctx | {'roots': balance_roots,
- 'valid': valid,
- 'booking': self.server.bookings[id_]})
- elif self.pagename == 'bookings':
+ if self.pagename == 'bookings':
self.redirect(
- Path('/').joinpath('edit_structured').joinpath(str(id_)))
- elif self.pagename == 'edit_structured':
- ctx['dat_lines'] = [
- dl.as_dict for dl
- in self.server.bookings[ctx['id']].booked_lines]
- self.send_rendered(Path('edit_structured.tmpl'), ctx)
- elif self.pagename == 'edit_raw':
- ctx['dat_lines'] = self.server.bookings[ctx['id']].booked_lines
- self.send_rendered(Path('edit_raw.tmpl'), ctx)
- elif self.pagename == 'ledger_raw':
- self.send_rendered(Path('ledger_raw.tmpl'),
- ctx | {'dat_lines': self.server.dat_lines})
+ Path('/').joinpath(EDIT_STRUCT).joinpath(self.path_toks[2]))
+ elif self.pagename == 'balance':
+ self.get_balance(ctx)
+ elif self.pagename.startswith(PREFIX_EDIT):
+ self.get_edit(ctx, self.pagename == EDIT_RAW)
+ elif self.pagename.startswith(PREFIX_LEDGER):
+ self.get_ledger(ctx, self.pagename == LEDGER_RAW)
else:
- self.send_rendered(
- Path('ledger_structured.tmpl'),
- ctx | {'dat_lines': self.server.dat_lines_sans_empty})
+ self.get_ledger(ctx, False)
+
+ def get_balance(self, ctx) -> None:
+ """Display tree of calculated Accounts over .bookings[:up_incl+1]."""
+ id_ = int(self.params.first('up_incl') or '-1')
+ valid = True
+ account_names = set()
+ to_balance = (self.server.bookings[:id_ + 1] if id_ >= 0
+ else self.server.bookings)
+ for booking in to_balance:
+ valid = valid if not booking.is_questionable else False
+ 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 to_balance:
+ for account_name in booking.account_changes:
+ full_names_to_accounts[account_name].local_wealth +=\
+ booking.account_changes[account_name]
+ ctx['roots'] = [ac for ac in full_names_to_accounts.values()
+ if not ac.parent]
+ ctx['valid'] = valid
+ ctx['booking'] = self.server.bookings[id_]
+ self._send_rendered('balance', ctx)
+
+ def get_edit(self, ctx, raw: bool) -> None:
+ """Display edit form for individual Booking."""
+ id_ = int(self.path_toks[2])
+ ctx['id'] = id_
+ ctx['dat_lines'] = [dl if raw else dl.as_dict for dl
+ in self.server.bookings[id_].booked_lines]
+ self._send_rendered(EDIT_RAW if raw else EDIT_STRUCT, ctx)
+
+ def get_ledger(self, ctx: dict[str, Any], raw: bool) -> None:
+ """Display ledger of all Bookings."""
+ ctx['dat_lines'] = [dl for dl in self.server.dat_lines
+ if raw or not dl.is_empty]
+ self._send_rendered(LEDGER_RAW if raw else LEDGER_STRUCT, ctx)
class Server(PlomHttpServer):
self._recalc_dat_lines()
return new_id
- @property
- def dat_lines_sans_empty(self) -> list[DatLine]:
- """Return only those .data_lines with .code or .comment."""
- return [dl for dl in self.dat_lines if not dl.is_empty]
-
- def balance_roots(self, up_incl: int) -> tuple[bool, list[Account]]:
- """Return tree of calculated Accounts over .bookings[:up_incl+1]."""
- account_names = set()
- valid = True
- to_balance = (self.bookings[:up_incl + 1] if up_incl >= 0
- else self.bookings)
- for booking in to_balance:
- valid = valid if not booking.is_questionable else False
- 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 to_balance:
- for account_name in booking.account_changes:
- full_names_to_accounts[account_name].local_wealth +=\
- booking.account_changes[account_name]
- return valid, [ac for ac in full_names_to_accounts.values()
- if not ac.parent]
-
if __name__ == "__main__":
if not LEDGER_DAT: