PATH_TEMPLATES = Path('templates')
_PREFIX_EDIT = 'edit'
_PREFIX_FILE = 'file'
-PREFIX_LEDGER = 'ledger'
+_PREFIX_LEDGER = 'ledger'
_SERVER_HOST = '127.0.0.1'
_SERVER_PORT = 8084
-_SUFFIX_RAW = 'raw'
+SUFFIX_HTML = '.html'
+SUFFIX_JS = '.js'
_SUFFIX_STRUCTURED = 'structured'
-PAGENAME_BALANCE = 'balance'
-PAGENAME_EDIT_RAW = f'{_PREFIX_EDIT}_{_SUFFIX_RAW}'
-PAGENAME_EDIT_STRUCTURED = f'{_PREFIX_EDIT}_{_SUFFIX_STRUCTURED}'
-PAGENAME_LEDGER_RAW = f'{PREFIX_LEDGER}_{_SUFFIX_RAW}'
-PAGENAME_LEDGER_STRUCTURED = f'{PREFIX_LEDGER}_{_SUFFIX_STRUCTURED}'
+_PAGENAME_EDIT_STRUCTURED = f'{_PREFIX_EDIT}_{_SUFFIX_STRUCTURED}'
def render_ctx_on_path(ledger, short_path_toks):
prefix, suffix = (pagename + '_').split('_')[:2]
method = getattr(ledger, f'view_ctx_{prefix}')
method_kwargs = {}
- if prefix != PREFIX_LEDGER:
+ if prefix != _PREFIX_LEDGER:
method_kwargs['id_'] = -1
if len(short_path_toks) > 1:
id_str = short_path_toks[1] or '-1'
if id_str.isdigit() or (id_str[1:].isdigit() and id_str[0] == '-'):
method_kwargs['id_'] = int(id_str)
if prefix == _PREFIX_EDIT:
- method_kwargs['raw'] = suffix == _SUFFIX_RAW
+ method_kwargs['raw'] = suffix != _SUFFIX_STRUCTURED
return method(**method_kwargs)
'Handles HTTP requests.'
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
'Route POST requests to respective handlers.'
redir_target = Path(self.path)
elif self.pagename.startswith(_PREFIX_EDIT)\
and self.postvars.first('apply'):
redir_target = self.post_edit()
- elif self.pagename.startswith(PREFIX_LEDGER):
+ elif self.pagename.startswith(_PREFIX_LEDGER):
redir_target = self.post_ledger_action()
self.redirect(redir_target)
'Based on postvars, edit targeted Booking.'
old_id = int(self.path_toks[2])
new_lines = []
- if self.pagename == PAGENAME_EDIT_STRUCTURED:
+ if self.pagename == _PAGENAME_EDIT_STRUCTURED:
line_keys = self.postvars.keys_prefixed('line_')
lineno_to_inputs: dict[int, list[str]] = {}
for key in line_keys:
return Path(self.path)
if 'add_booking' in self.postvars.as_dict:
id_ = self.server.ledger.add_empty_block()
- return Path('/', PAGENAME_EDIT_STRUCTURED, f'{id_}')
- keys_prefixed = self.postvars.keys_prefixed(PREFIX_LEDGER)
+ return Path('/', _PAGENAME_EDIT_STRUCTURED, f'{id_}')
+ keys_prefixed = self.postvars.keys_prefixed(_PREFIX_LEDGER)
action, id_str = keys_prefixed[0].split('_', maxsplit=2)[1:]
id_ = int(id_str)
if action.startswith('move'):
'Route GET requests to respective handlers.'
if self.pagename == 'blocks':
self.redirect(
- Path('/', PAGENAME_EDIT_STRUCTURED, self.path_toks[2]))
+ Path('/', _PAGENAME_EDIT_STRUCTURED, self.path_toks[2]))
return
ctx: dict[str, Any] = {}
- if self.pagename.endswith('.js'):
- if self.pagename.startswith(PAGENAME_EDIT_STRUCTURED):
+ if self.pagename.endswith(SUFFIX_JS):
+ if self.pagename.startswith(_PAGENAME_EDIT_STRUCTURED):
block = self.server.ledger.blocks[int(self.path_toks[2])]
ctx['raw_gap_lines'] = [dl.raw for dl in block.gap.lines]
ctx['booking_lines'] = (
ctx['path'] = self.path
ctx['unsaved_changes'] = self.server.ledger.tainted
ctx |= render_ctx_on_path(self.server.ledger, self.path_toks[1:])
- self._send_rendered(self.pagename, ctx)
+ self.send_rendered(Path(f'{self.pagename}{SUFFIX_HTML}'), ctx)
# built-ins
from sys import exit as sys_exit
from pathlib import Path
-from typing import Optional
+from typing import Any, Optional
# requirements.txt
from jinja2 import (Environment as JinjaEnv,
FileSystemLoader as JinjaFSLoader)
# ourselves
-from ledgplom.http import (
- PAGENAME_BALANCE, PAGENAME_EDIT_RAW, PAGENAME_EDIT_STRUCTURED,
- PAGENAME_LEDGER_RAW, PAGENAME_LEDGER_STRUCTURED, PATH_TEMPLATES,
- render_ctx_on_path)
+from ledgplom.http import (PATH_TEMPLATES, SUFFIX_HTML, SUFFIX_JS,
+ render_ctx_on_path)
from ledgplom.ledger import Ledger
jinja = JinjaEnv(loader=JinjaFSLoader(PATH_TEMPLATES),
autoescape=True,
trim_blocks=True)
- templates = {item: jinja.get_template(f'{item}.tmpl')
- for item in (PAGENAME_BALANCE,
- PAGENAME_EDIT_RAW,
- PAGENAME_EDIT_STRUCTURED,
- PAGENAME_LEDGER_RAW,
- PAGENAME_LEDGER_STRUCTURED)}
paths = tuple(_PATH_TESTS.iterdir())
for dat_path in [p for p in paths if p.parts[-1].endswith(_EXT_DAT)]:
ledger = Ledger(dat_path)
and p.parts[-1].startswith(
str(dat_path.parts[-1])[:-len(_EXT_DAT)] + '.')]:
path_toks = str(test_path.parts[-1]).split('.')[1:]
- lines_rendered = templates[path_toks[0]].render(
- render_ctx_on_path(ledger, path_toks)).split('\n')
+ tmpl_name = path_toks[0]
+ ctx: dict[str, Any] = {}
+ if tmpl_name.endswith(SUFFIX_JS.replace('.', '_')):
+ tmpl_name = tmpl_name[:-len(SUFFIX_JS)] + SUFFIX_JS
+ else:
+ tmpl_name += SUFFIX_HTML
+ ctx |= render_ctx_on_path(ledger, path_toks)
+ lines_rendered = jinja.get_template(tmpl_name).render(ctx
+ ).split('\n')
with test_path.open('r', encoding='utf8') as f:
lines_expected = tuple(line.rstrip('\n')
for line in f.readlines())
--- /dev/null
+{% extends '_base.tmpl' %}
+
+
+
+{% block css %}
+{{ macros.css_tabular_money() }}
+{{ macros.css_balance() }}
+td.money table {
+ float: left;
+}
+summary::marker {
+{{ macros.css_noninput_monospace() -}}
+}
+summary {
+ list-style-type: "[…]";
+}
+details[open] > summary {
+ list-style-type: "[^]";
+}
+span.indent {
+ letter-spacing: 3em;
+}
+{% endblock css %}
+
+
+
+{% macro account_with_children(block_id, account, name_indent) %}
+
+{%- macro _(code_indent=1) %}
+{{- " " * 4 * code_indent -}}
+{% endmacro %}
+
+{%- macro tr_money_balance(amount, currency, i=4) %}
+{{_(i)}}<tr>
+{{_(i)}} <td class="balance amount">
+ {{- amount -}}
+ </td>
+{{_(i)}} <td class="balance currency">
+ {{- macros.currency_short(currency) -}}
+ </td>
+{{_(i)}}</tr>
+{% endmacro %}
+
+{%- if account.get_wealth_at(block_id).moneys | length > 0 %}
+{{_()}}<tr>
+{{_()}} <td class="money">
+{##}{% if account.get_wealth_at(block_id).moneys | length == 1 %}
+{{_()}} <table>
+{######}{% for cur, amt in account.get_wealth_at(block_id).moneys.items() %}
+ {{- tr_money_balance(amt, cur) -}}
+{######}{% endfor %}
+{{_()}} </table>
+{##}{% else %}
+{{_()}} <details>
+{{_()}} <summary>
+{{_()}} <table>
+{######}{% for cur, amt in account.get_wealth_at(block_id).moneys.items() %}
+ {{- tr_money_balance(amt, cur, i=6)
+ if 1 == loop.index -}}
+{######}{% endfor %}
+{{_()}} </table>
+{{_()}} </summary>
+{{_()}} <table>
+{######}{% for cur, amt in account.get_wealth_at(block_id).moneys.items() %}
+ {{- tr_money_balance(amt, cur, 5) if 1 < loop.index -}}
+{######}{% endfor %}
+{{_()}} </table>
+{{_()}} </details>
+{##}{% endif %}
+{{_()}} </td>
+{{_()}} <td>
+{{_()}} <span class="indent">{{ ' '|safe * name_indent }}</span>
+ {{- ":" if account.parent }}
+ {{- account.basename }}
+ {{- ":" if account.children }}
+{{_()}} </td>
+{{_()}} <td>{{ account.desc }}</td>
+{{_()}}</tr>
+{##}{% for child in account.children_euro_sorted_at(block_id) %}
+ {{- account_with_children(block_id, child, name_indent=name_indent+1) -}}
+{##}{% endfor %}
+{% endif %}
+{% endmacro %}
+
+
+
+{% block content %}
+<p>
+ {{ macros.conditional_block_nav('/balance/', 'prev', block) -}}{##}
+ {{ macros.conditional_block_nav('/balance/', 'next', block) }}{##}
+ |
+ balance after {# -#}
+ <a href="/blocks/{{block.id_}}">{# -#}
+ booking {{block.id_}} {# -#}
+ ({{ block.booking.date }}: {{ block.booking.target }}){# -#}
+ </a>
+</p>
+<table class="alternating {% if not valid %}critical{% endif %}">
+{% for root in roots %}
+{{ account_with_children(block.id_, root, name_indent=0) -}}
+{% endfor %}
+</table>
+{% endblock %}
+++ /dev/null
-{% extends '_base.tmpl' %}
-
-
-
-{% block css %}
-{{ macros.css_tabular_money() }}
-{{ macros.css_balance() }}
-td.money table {
- float: left;
-}
-summary::marker {
-{{ macros.css_noninput_monospace() -}}
-}
-summary {
- list-style-type: "[…]";
-}
-details[open] > summary {
- list-style-type: "[^]";
-}
-span.indent {
- letter-spacing: 3em;
-}
-{% endblock css %}
-
-
-
-{% macro account_with_children(block_id, account, name_indent) %}
-
-{%- macro _(code_indent=1) %}
-{{- " " * 4 * code_indent -}}
-{% endmacro %}
-
-{%- macro tr_money_balance(amount, currency, i=4) %}
-{{_(i)}}<tr>
-{{_(i)}} <td class="balance amount">
- {{- amount -}}
- </td>
-{{_(i)}} <td class="balance currency">
- {{- macros.currency_short(currency) -}}
- </td>
-{{_(i)}}</tr>
-{% endmacro %}
-
-{%- if account.get_wealth_at(block_id).moneys | length > 0 %}
-{{_()}}<tr>
-{{_()}} <td class="money">
-{##}{% if account.get_wealth_at(block_id).moneys | length == 1 %}
-{{_()}} <table>
-{######}{% for cur, amt in account.get_wealth_at(block_id).moneys.items() %}
- {{- tr_money_balance(amt, cur) -}}
-{######}{% endfor %}
-{{_()}} </table>
-{##}{% else %}
-{{_()}} <details>
-{{_()}} <summary>
-{{_()}} <table>
-{######}{% for cur, amt in account.get_wealth_at(block_id).moneys.items() %}
- {{- tr_money_balance(amt, cur, i=6)
- if 1 == loop.index -}}
-{######}{% endfor %}
-{{_()}} </table>
-{{_()}} </summary>
-{{_()}} <table>
-{######}{% for cur, amt in account.get_wealth_at(block_id).moneys.items() %}
- {{- tr_money_balance(amt, cur, 5) if 1 < loop.index -}}
-{######}{% endfor %}
-{{_()}} </table>
-{{_()}} </details>
-{##}{% endif %}
-{{_()}} </td>
-{{_()}} <td>
-{{_()}} <span class="indent">{{ ' '|safe * name_indent }}</span>
- {{- ":" if account.parent }}
- {{- account.basename }}
- {{- ":" if account.children }}
-{{_()}} </td>
-{{_()}} <td>{{ account.desc }}</td>
-{{_()}}</tr>
-{##}{% for child in account.children_euro_sorted_at(block_id) %}
- {{- account_with_children(block_id, child, name_indent=name_indent+1) -}}
-{##}{% endfor %}
-{% endif %}
-{% endmacro %}
-
-
-
-{% block content %}
-<p>
- {{ macros.conditional_block_nav('/balance/', 'prev', block) -}}{##}
- {{ macros.conditional_block_nav('/balance/', 'next', block) }}{##}
- |
- balance after {# -#}
- <a href="/blocks/{{block.id_}}">{# -#}
- booking {{block.id_}} {# -#}
- ({{ block.booking.date }}: {{ block.booking.target }}){# -#}
- </a>
-</p>
-<table class="alternating {% if not valid %}critical{% endif %}">
-{% for root in roots %}
-{{ account_with_children(block.id_, root, name_indent=0) -}}
-{% endfor %}
-</table>
-{% endblock %}
--- /dev/null
+{% extends '_base.tmpl' %}
+
+
+
+{% block css %}
+{{ macros.css_tabular_money() }}
+{{ macros.css_booking_balance() }}
+{%- endblock %}
+
+
+
+{% block script %}
+<script type="module" src="/taint.js">
+</script>
+{% endblock %}
+
+
+
+{% block content %}
+{{ macros.edit_bar(block, 'raw', 'structured') }}
+<textarea name="raw_lines"
+ class="tainter"
+ cols=100
+ rows={{ block.lines|length + 1 }}
+ >
+{% for dat_line in block.lines %}
+{{ dat_line.raw }}
+{% endfor %}
+</textarea>
+</form>
+{{ macros.booking_balance(roots, valid) }}
+{% endblock %}
+++ /dev/null
-{% extends '_base.tmpl' %}
-
-
-
-{% block css %}
-{{ macros.css_tabular_money() }}
-{{ macros.css_booking_balance() }}
-{%- endblock %}
-
-
-
-{% block script %}
-<script type="module" src="/taint.js">
-</script>
-{% endblock %}
-
-
-
-{% block content %}
-{{ macros.edit_bar(block, 'raw', 'structured') }}
-<textarea name="raw_lines"
- class="tainter"
- cols=100
- rows={{ block.lines|length + 1 }}
- >
-{% for dat_line in block.lines %}
-{{ dat_line.raw }}
-{% endfor %}
-</textarea>
-</form>
-{{ macros.booking_balance(roots, valid) }}
-{% endblock %}
--- /dev/null
+{% extends '_base.tmpl' %}
+
+
+
+{% block css %}
+{{ macros.css_tabular_money() }}
+{{ macros.css_booking_balance() }}
+input.amount {
+ text-align: right;
+ font-family: monospace;
+}
+#date_input {
+ margin-right: 0.3em;
+ font-family: monospace;
+}
+{% endblock %}
+
+
+
+{% block script %}
+<script type="module" src="/edit_structured.js/{{ block.id_ }}">
+</script>
+{% endblock %}
+
+
+
+{% block content %}
+{{ macros.edit_bar(block, 'structured', 'raw') }}
+<span class="disable_on_taint">
+<input id="btn_mirror" type="button" value="mirror">
+<input id="btn_sink" type="button" value="fill sink">
+|
+<input id="btn_replace" type="button" value="replace string">
+from
+<input id="replace_from" />
+to
+<input id="replace_to" />
+</span>
+<hr />
+<table id="booking_lines" class="alternating">
+</table>
+<div>Gap:</div>
+<textarea id="gap_lines"
+ name="raw_lines"
+ cols=100
+ rows={{ raw_gap_lines|length + 1 }}
+ class="tainter"
+ >
+</textarea>
+</form>
+<datalist id="all_accounts">
+{% for acc in all_accounts %}
+<option value="{{ acc }}">{{ acc }}</a>
+{% endfor %}
+</datalist>
+<hr />
+{{ macros.booking_balance(roots, valid) }}
+{% endblock %}
+++ /dev/null
-{% extends '_base.tmpl' %}
-
-
-
-{% block css %}
-{{ macros.css_tabular_money() }}
-{{ macros.css_booking_balance() }}
-input.amount {
- text-align: right;
- font-family: monospace;
-}
-#date_input {
- margin-right: 0.3em;
- font-family: monospace;
-}
-{% endblock %}
-
-
-
-{% block script %}
-<script type="module" src="/edit_structured.js/{{ block.id_ }}">
-</script>
-{% endblock %}
-
-
-
-{% block content %}
-{{ macros.edit_bar(block, 'structured', 'raw') }}
-<span class="disable_on_taint">
-<input id="btn_mirror" type="button" value="mirror">
-<input id="btn_sink" type="button" value="fill sink">
-|
-<input id="btn_replace" type="button" value="replace string">
-from
-<input id="replace_from" />
-to
-<input id="replace_to" />
-</span>
-<hr />
-<table id="booking_lines" class="alternating">
-</table>
-<div>Gap:</div>
-<textarea id="gap_lines"
- name="raw_lines"
- cols=100
- rows={{ raw_gap_lines|length + 1 }}
- class="tainter"
- >
-</textarea>
-</form>
-<datalist id="all_accounts">
-{% for acc in all_accounts %}
-<option value="{{ acc }}">{{ acc }}</a>
-{% endfor %}
-</datalist>
-<hr />
-{{ macros.booking_balance(roots, valid) }}
-{% endblock %}
--- /dev/null
+{% extends '_base.tmpl' %}
+
+
+
+{% block css %}
+{{ macros.css_ledger() }}
+table {
+ font-family: monospace;
+}
+{% endblock %}
+
+
+
+{% block content %}
+<form action="/ledger_raw" method="POST">
+{{ macros.ledger_empty_lines_fix(has_redundant_empty_lines) -}}
+<table class="alternating">
+{% for block in blocks %}
+{{ macros.ledger_block_columns('raw', block) -}}
+{##}{% for line in block.lines %}
+ <tr>{# -#}
+ <td{{ ' class="critical"'|safe if line.errors }}>
+ {{- line.raw }} {# -#}
+ </td>{# -#}
+ </tr>
+{##}{% endfor %}
+{% endfor %}
+</table>
+</form>
+{% endblock %}
+++ /dev/null
-{% extends '_base.tmpl' %}
-
-
-
-{% block css %}
-{{ macros.css_ledger() }}
-table {
- font-family: monospace;
-}
-{% endblock %}
-
-
-
-{% block content %}
-<form action="/ledger_raw" method="POST">
-{{ macros.ledger_empty_lines_fix(has_redundant_empty_lines) -}}
-<table class="alternating">
-{% for block in blocks %}
-{{ macros.ledger_block_columns('raw', block) -}}
-{##}{% for line in block.lines %}
- <tr>{# -#}
- <td{{ ' class="critical"'|safe if line.errors }}>
- {{- line.raw }} {# -#}
- </td>{# -#}
- </tr>
-{##}{% endfor %}
-{% endfor %}
-</table>
-</form>
-{% endblock %}
--- /dev/null
+{% extends '_base.tmpl' %}
+
+
+
+{% block css %}
+{{ macros.css_ledger() }}
+{{ macros.css_tabular_money() }}
+{% endblock %}
+
+
+
+{% block content %}
+<form action="/ledger_structured" method="POST">
+{{ macros.ledger_empty_lines_fix(has_redundant_empty_lines) -}}
+<table class="alternating">
+{% for block in blocks %}
+{{ macros.ledger_block_columns('structured', block) -}}
+{##}{% if block.booking %}
+ <tr>
+ <td colspan=3{{ ' class="critical"'
+ if block.booking.intro_line.errors }}>
+ {{- block.booking.date }} {{ block.booking.target -}}
+ </td>
+ <td>{{ block.booking.intro_line.comment }}</td>
+ </tr>
+{####}{% for line in block.booking.transfer_lines %}
+ <tr>
+ <td class="amount{{ ' critical' if line.errors }}">
+ {{- line.amount_short -}}
+ </td>
+ <td class="currency{{ ' critical' if line.errors }}">
+ {{- macros.currency_short(line.currency) -}}
+ </td>
+ <td{{ ' class="critical"'|safe if line.errors }}>
+ {{- line.account -}}
+ </td>
+ <td>{{ line.comment }}</td>
+ </tr>
+{####}{% endfor %}
+{##}{% endif %}
+{##}{% for line in block.gap.lines %}
+ <tr>
+ <td colspan=4>{{ line.raw }} </td>
+ </tr>
+{##}{% endfor %}
+{% endfor %}
+</table>
+<input type="submit" name="add_booking" value="add booking" />
+</form>
+{% endblock %}
+++ /dev/null
-{% extends '_base.tmpl' %}
-
-
-
-{% block css %}
-{{ macros.css_ledger() }}
-{{ macros.css_tabular_money() }}
-{% endblock %}
-
-
-
-{% block content %}
-<form action="/ledger_structured" method="POST">
-{{ macros.ledger_empty_lines_fix(has_redundant_empty_lines) -}}
-<table class="alternating">
-{% for block in blocks %}
-{{ macros.ledger_block_columns('structured', block) -}}
-{##}{% if block.booking %}
- <tr>
- <td colspan=3{{ ' class="critical"'
- if block.booking.intro_line.errors }}>
- {{- block.booking.date }} {{ block.booking.target -}}
- </td>
- <td>{{ block.booking.intro_line.comment }}</td>
- </tr>
-{####}{% for line in block.booking.transfer_lines %}
- <tr>
- <td class="amount{{ ' critical' if line.errors }}">
- {{- line.amount_short -}}
- </td>
- <td class="currency{{ ' critical' if line.errors }}">
- {{- macros.currency_short(line.currency) -}}
- </td>
- <td{{ ' class="critical"'|safe if line.errors }}>
- {{- line.account -}}
- </td>
- <td>{{ line.comment }}</td>
- </tr>
-{####}{% endfor %}
-{##}{% endif %}
-{##}{% for line in block.gap.lines %}
- <tr>
- <td colspan=4>{{ line.raw }} </td>
- </tr>
-{##}{% endfor %}
-{% endfor %}
-</table>
-<input type="submit" name="add_booking" value="add booking" />
-</form>
-{% endblock %}