'Collect directly HTTP-related elements.'
-# standard libs
+# built-ins
from pathlib import Path
from typing import Any
-# non-standard libs
+# plomlib
from plomlib.web import PlomHttpHandler, PlomHttpServer, PlomQueryMap
+# ourselves
from ledgplom.ledger import Account, DatBlock, DEFAULT_INDENT, Ledger
-_PATH_TEMPLATES = Path('templates')
+PATH_TEMPLATES = Path('templates')
_PREFIX_EDIT = 'edit_'
_PREFIX_FILE = 'file_'
_PREFIX_LEDGER = 'ledger_'
_PAGENAME_EDIT_RAW = f'{_PREFIX_EDIT}{_TOK_RAW}'
_PAGENAME_EDIT_STRUCTURED = f'{_PREFIX_EDIT}{_TOK_STRUCTURED}'
_PAGENAME_LEDGER_RAW = f'{_PREFIX_LEDGER}{_TOK_RAW}'
-_PAGENAME_LEDGER_STRUCTURED = f'{_PREFIX_LEDGER}{_TOK_STRUCTURED}'
+PAGENAME_LEDGER_STRUCTURED = f'{_PREFIX_LEDGER}{_TOK_STRUCTURED}'
class Server(PlomHttpServer):
'Extends parent by loading .dat file into database for Handler.'
def __init__(self, path_dat: Path, *args, **kwargs) -> None:
- super().__init__(_PATH_TEMPLATES,
+ super().__init__(PATH_TEMPLATES,
(_SERVER_HOST, _SERVER_PORT),
_Handler)
self.ledger = Ledger(path_dat)
self.redirect(
Path('/', _PAGENAME_EDIT_STRUCTURED, self.path_toks[2]))
return
+ ### from time import time_ns
+ ### start = time_ns()
ctx = {'unsaved_changes': self.server.ledger.tainted,
'path': self.path}
if self.pagename == 'balance':
self.get_ledger(ctx, self.pagename == _PAGENAME_LEDGER_RAW)
else:
self.get_ledger(ctx, False)
+ ### end = time_ns()
+ ### duration = (end - start) / (10 ** 9)
+ ### print("DEBUG GET", self.pagename, f'{duration:8.5f}')
def get_balance(self, ctx) -> None:
'Display tree of calculated Accounts over blocks up_incl+1.'
ctx['has_redundant_empty_lines'] =\
self.server.ledger.has_redundant_empty_lines
self._send_rendered(
- _PAGENAME_LEDGER_RAW if raw else _PAGENAME_LEDGER_STRUCTURED, ctx)
+ _PAGENAME_LEDGER_RAW if raw else PAGENAME_LEDGER_STRUCTURED, ctx)
--- /dev/null
+'To run tests.'
+# built-ins
+from sys import exit as sys_exit
+from pathlib import Path
+from typing import Optional
+# requirements.txt
+from jinja2 import (Environment as JinjaEnv,
+ FileSystemLoader as JinjaFSLoader)
+# ourselves
+from ledgplom.http import PAGENAME_LEDGER_STRUCTURED, PATH_TEMPLATES
+from ledgplom.ledger import Ledger
+
+
+_EXT_DAT = '.dat'
+_EXT_HTML = '.html'
+_PATH_TESTS = Path('tests')
+
+
+def run_tests() -> None:
+ 'Run tests from tests directory.'
+ def fail(abort_msg: str, msg_prefix: str, idx: Optional[int]) -> None:
+ for jdx, line in enumerate(lines_rendered[:idx], start=1):
+ print(f'{jdx}: [{line}]')
+ print(f'{msg_prefix} FAILED – {abort_msg}')
+ sys_exit(1)
+
+ jinja = JinjaEnv(loader=JinjaFSLoader(PATH_TEMPLATES), autoescape=True)
+ tmpl = jinja.get_template(f'{PAGENAME_LEDGER_STRUCTURED}.tmpl')
+ for path in [p for p in _PATH_TESTS.iterdir()
+ if p.parts[-1].endswith(_EXT_DAT)]:
+ with Path(str(path)[:-len(_EXT_DAT)] + _EXT_HTML
+ ).open('r', encoding='utf8') as f:
+ lines_expected = [line.rstrip('\n') for line in f.readlines()]
+ lines_rendered = tmpl.render(blocks=Ledger(path).blocks).split('\n')
+ msg_prefix = f'test for {path}:'
+ for idx0, line in enumerate(lines_rendered):
+ idx1 = idx0 + 1
+ abort_msg = ''
+ if idx1 > len(lines_expected):
+ abort_msg = f'only {idx0} lines expected'
+ elif lines_expected[idx0] != line:
+ abort_msg = f'line differs, expected: [{lines_expected[idx0]}]'
+ if abort_msg:
+ fail(abort_msg, msg_prefix, idx1)
+ if len(lines_expected) > idx1:
+ fail(f'more lines expected line {idx1}', msg_prefix, None)
+ print(f'{msg_prefix} passed')
#!/usr/bin/env python3
'Viewer and editor for ledger .dat files.'
-# standard libs
+# built-ins
from os import environ
from pathlib import Path
-from sys import exit as sys_exit
-# non-standard libs
+from sys import argv, exit as sys_exit
+# plomlib
from plomlib.setup import dependency_hint
+# ourselves
try:
from ledgplom.http import Server
+ from ledgplom.testing import run_tests
except ModuleNotFoundError as e:
dependency_hint(e)
-LEDGER_DAT = environ.get('LEDGER_DAT')
+_ARG_TEST = 'test'
+_NAME_ENV_LEDGER_DAT = 'LEDGER_DAT'
+_LEDGER_DAT = environ.get(_NAME_ENV_LEDGER_DAT)
if __name__ == '__main__':
- if not LEDGER_DAT:
- print('LEDGER_DAT environment variable not set.')
+ if len(argv) == 1:
+ if not _LEDGER_DAT:
+ print(f'{_NAME_ENV_LEDGER_DAT} environment variable not set.')
+ sys_exit(1)
+ Server(Path(_LEDGER_DAT)).serve()
+ elif len(argv) == 2:
+ if argv[1] != _ARG_TEST:
+ print('fail: unknown arg argument')
+ sys_exit(1)
+ run_tests()
+ else:
+ print('fail: expected zero or one arguments')
sys_exit(1)
- Server(Path(LEDGER_DAT)).serve()
--- /dev/null
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+</script>
+<style>
+html {
+ scroll-padding-top: 2em;
+}
+body {
+ background: #ffffff;
+ font-family: sans-serif;
+ text-align: left;
+ margin: 0;
+ padding: 0;
+}
+#header {
+ background: #ffffff;
+ position: sticky;
+ top: 0;
+ padding-left: 0.5em;
+ padding-bottom: 0.25em;
+ border-bottom: 1px solid black;
+}
+table.alternating > tbody > tr:nth-child(odd) {
+ background-color: #dcdcdc;
+}
+table.alternating > tbody > tr:nth-child(even) {
+ background: #ffffff;;
+}
+td {
+ vertical-align: top;
+}
+div.critical, td.critical, tr.critical, span.critical, input[type="submit"].critical {
+ background: #ff6666;
+}
+
+
+td.block_column {
+ background: #ffffff;
+}
+td.block_column.critical {
+ background: #ff6666;
+}
+
+
+td.amount {
+ text-align: right;
+}
+td.amount, td.currency {
+
+ font-family: monospace;
+ font-size: 1.25em;
+
+}
+
+td.amount {
+ text-align: right;
+}
+td.amount, td.currency {
+ vertical-align: bottom;
+}
+
+</style>
+</head>
+<body>
+
+<div id="header">
+<form action="" method="POST">
+<span class="disable_on_change">
+ledger <a href="/ledger_structured">structured</a>
+/ <a href="/ledger_raw">raw</a>
+· <a href="/balance">balance</a>
+·
+
+<input type="submit"name="file_load" value="reload" />
+
+</span>
+</form>
+</div>
+
+
+<form action="/ledger_structured" method="POST">
+
+
+
+<table class="alternating">
+
+
+<tr></tr><!-- just to keep the background-color alternation in proper order -->
+<tr id="block_0">
+<td class="block_column " rowspan=2>
+<input type="submit" name="ledger_moveup_0" value="^" disabled/><br />
+<input type="submit" name="ledger_movedown_0" value="v" disabled/><br />
+<input type="submit" name="ledger_copy_0" value="C" />
+</td>
+<td class="block_column " rowspan=2>
+[<a href="#block_0">#</a>]<br />
+[<a href="/balance?up_incl=0">b</a>]<br />
+[<a href="/edit_structured/0">e</a>]
+</td>
+</tr >
+
+
+
+ <tr>
+ <td colspan=4> </td>
+ </tr>
+
+
+</table>
+<input type="submit" name="add_booking" value="add booking" />
+</form>
+
+</body>
+</html>