-from http.server import BaseHTTPRequestHandler, HTTPServer
import os
import json
import datetime
+import jinja2
+from plomlib import PlomDB, PlomException, run_server, run_server, PlomServer
-hostName = "localhost"
-serverPort = 8081
+db_path = '/home/plom/org/calories_db.json'
-def build_page(eatable_rows, consumption_rows, eatables_selection, day_rows):
- return """<html>
-<meta charset="UTF-8">
+server_port = 8081
+
+tmpl = """
<style>
table { margin-bottom: 2em; }
th, td { text-align: left; }
td.number { text-align: right; }
input[type="number"] { text-align: right; }
-</style>""" + f"""
+</style>
<body>
<form action="/" method="POST">
<td><input name="update" type="submit" value="update" /></td>
<table>
<tr><th>eatable</th><th>unit count</th><th>unit weight (g)</th><th>calories</th><th>sugar (g)</th></tr>
-{consumption_rows}
+{% for c in consumptions %}
+<tr>
+<input type="hidden" name="keep_visible" value="1"><input name="eatable_key" type="hidden" value="{{c.key|e}}">
+<td class="number"><input class="unit_count number" name="unit_count" type="number" min="0" step="0.1" value="{{c.count}}" /></td>
+<td>{{c.title}}</td>
+<td></td>
+<td class="number">{{c.cals}}</td>
+<td class="number">{{c.sugar}}</td>
+</tr>
+{% endfor %}
<tr>
<th>add from DB:</th>
</tr>
<tr>
<input type="hidden" name="keep_visible" value="0">
<td class="number"><input class="unit_count" name="unit_count" type="number" step="0.1" min="0" value="0" /></td>
-<td><select name="eatable_key">{eatables_selection}</select></td>
+<td><select name="eatable_key">{% for sel in eatables_selection %}
+<option value="{{sel.0|e}}">{{sel.1|e}}</option>
+{% endfor %}</select></td>
<td></td>
</tr>
</table>
<table>
+<tr><th>today:</th><th></th><th></th><th>archive?</th></tr>
+<td><input name="new_date" size=8 value="{{db.today_date}}" /><td>
+<td class="number"><input name="new_day_cals" type="hidden" value="{{db.today.calories}}" readonly />{{db.today.calories}}</td>
+<td class="number"><input name="new_day_sugar" type="hidden" value="{{db.today.sugar_g}}" readonly />{{db.today.sugar_g}}</td>
+<td><input name="archive_day" type="checkbox" /></td>
+</tr>
<tr><th>day</th><th>calories</th><th>sugar (g)</th></tr>
-{day_rows}
+{% for d in days %}
+<tr>
+<td><input name="day_date" type="hidden" value="{{d.date|e}}" />{{d.date_short|e}}</td>
+<td class="number"><input name="day_cals" type="hidden" step="0.1" min="0" value="{{d.cals}}" />{{d.cals}}</td>
+<td class="number"><input name="day_sugar" type="hidden" step="0.1" min="0" value="{{d.sugar}}" />{{d.sugar}}</td>
+</tr>
+{% endfor %}
</table>
<table>
<tr><th>title</th><th>calories</th><th>sugar (g)</th><th>standard weight (g)</th><th>comments</th><th>delete</th></tr>
-{eatable_rows}
+{% for e in eatables %}
+<tr>
+<input name="eatable_uuid" type="hidden" value="{{e.uuid}}" />
+<td><input name="title" value="{{e.title|e}}" /></td>
+<td class="number"><input name="cals" type="number" step="0.1" min="0" value="{{e.cals}}" /></td>
+<td class="number"><input name="sugar_g" type="number" step="0.1" min="0" value="{{e.sugar_g}}" /></td>
+<td class="number"><input name="standard_g" type="number" step="0.1" min="0" value="{{e.sugar_g}}" /></td>
+<td><input name="comments" value="{{e.comments|e}}" /</td>
+<td><input name="delete" type="checkbox" value="{{e.uuid}}" />
+</tr>
+{% endfor %}
<tr>
<th>add:</th>
</tr>
</form>
</body>
<script>
-""" + """
var unit_count_inputs = document.getElementsByClassName("unit_count");
for (let i = 0; i < unit_count_inputs.length; i++) {
let input = unit_count_inputs[i];
}
</script>
-</html>
"""
-class LockFileDetected(Exception):
- pass
class Eatable:
"popularity": self.popularity
}
+
class Consumption:
def __init__(self, eatable_key, unit_count=None, keep_visible=0):
"keep_visible": self.keep_visible
}
+
class Day:
def __init__(self, calories, sugar_g):
"sugar_g": self.sugar_g,
}
-class Database:
+
+class CaloriesDB(PlomDB):
def __init__(self, load_from_file=True):
- db_name = "calories_db"
- self.db_file = db_name + ".json"
- self.lock_file = db_name+ ".lock"
+ self.load_from_file = load_from_file
self.eatables = {}
self.consumptions = []
self.days = {}
self.today = Day(0, 0)
self.today_date = ""
- if load_from_file and os.path.exists(self.db_file):
- with open(self.db_file, "r") as f:
- self.from_dict(json.load(f))
+ super().__init__(db_path)
+
+ def read_db_file(self, f):
+ if not self.load_from_file:
+ return
+ self.from_dict(json.load(f))
def from_dict(self, d):
self.set_today_date(d["today_date"])
return {"cals": calories, "sugar": sugar_g }
def eatables_selection(self):
- html = ''
+ options = []
already_selected = [c.eatable_key for c in self.consumptions]
for k, v in sorted(self.eatables.items(), key=lambda item: item[1].title):
if k in already_selected:
continue
v = self.eatables[k]
- html += '<option value="%s">%s</option>' % (k, v.title)
- return html
+ options += [(k, v.title)]
+ return options
def add_eatable(self, id_, eatable):
self.eatables[id_] = eatable
del self.eatables[id_]
def write(self):
- import shutil
- if os.path.exists(self.lock_file):
- raise LockFileDetected
- if os.path.exists(self.db_file):
- shutil.copy(self.db_file, self.db_file + ".bak")
- f = open(self.lock_file, "w+")
- f.close()
- with open(self.db_file, "w") as f:
- json.dump(self.to_dict(), f)
- os.remove(self.lock_file)
+ self.write_text_to_db(json.dumps(self.to_dict()))
-class MyServer(BaseHTTPRequestHandler):
+class CaloriesServer(PlomServer):
def do_POST(self):
from uuid import uuid4
from urllib.parse import parse_qs
length = int(self.headers['content-length'])
postvars = parse_qs(self.rfile.read(length).decode(), keep_blank_values=1)
- db = Database(False)
+ db = CaloriesDB(False)
def decode(key, i, is_num=True):
if is_num:
return float(postvars[key][i])
break
try:
db.write()
- self.send_response(302)
- self.send_header('Location', '/')
- self.end_headers()
- except LockFileDetected:
- self.send_response(400)
- self.end_headers()
- self.wfile.write(bytes("Sorry, lock file!", "utf-8"))
+ self.redirect()
+ except PlomException as e:
+ self.fail_400(e)
def do_GET(self):
- self.send_response(200)
- self.send_header("Content-type", "text/html")
- self.end_headers()
- db = Database()
-
- eatables = ""
+ db = CaloriesDB()
+ # eatables = ""
+ eatable_rows = []
for k,v in db.eatables.items():
- eatables += "<tr>"\
- "<input name=\"eatable_uuid\" type=\"hidden\" value=\"%s\" />"\
- "<td><input name=\"title\" value=\"%s\" /></td>"\
- "<td class\"number\"><input name=\"cals\" type=\"number\" step=\"0.1\" min=\"0\" value=\"%.1f\" /></td>"\
- "<td class\"number\"><input name=\"sugar_g\" type=\"number\" step=\"0.1\" min=\"0\" value=\"%.1f\" /></td>"\
- "<td class\"number\"><input name=\"standard_g\" type=\"number\" step=\"0.1\" min=\"1\" value=\"%1.f\" /></td>"\
- "<td><input name=\"comments\" value=\"%s\" /></td>"\
- "<td><input name=\"delete\" type=\"checkbox\" value=\"%s\" /></td>"\
- "</tr>" % (k, v.title, v.cals, v.sugar_g, v.standard_g, v.comments, k)
- consumptions = ""
+ eatable_rows += [{
+ 'uuid': k,
+ 'title': v.title,
+ 'cals': f'{v.cals:.1f}',
+ 'sugar_g': f'{v.sugar_g:.1f}',
+ 'standard_g': f'{v.standard_g:.1f}',
+ 'comments': v.comments
+ }]
db.consumptions = sorted(db.consumptions, key=lambda x: db.eatables[x.eatable_key].title)
+ consumption_rows = []
for c in db.consumptions:
r = db.calc_consumption(c)
- consumptions += "<tr />"\
- "<input type=\"hidden\" name=\"keep_visible\" value=\"1\"><input name=\"eatable_key\" type=\"hidden\" value=\"%s\">"\
- "<td class\"number\"><input class=\"unit_count number\" name=\"unit_count\" type=\"number\" min=\"0\" step=\"0.1\" value=\"%.1f\" /></td>"\
- "<td>%s</td>"\
- "<td></td>"\
- "<td class=\"number\">%.1f</td>"\
- "<td class=\"number\">%.1f</td>"\
- "</tr>" % (c.eatable_key, c.unit_count, db.eatables[c.eatable_key].title, r["cals"], r["sugar"])
- day_rows = ""
- for date in sorted(db.days.keys()):
+ consumption_rows += [{
+ 'key': c.eatable_key,
+ 'count': c.unit_count,
+ 'title': db.eatables[c.eatable_key].title,
+ 'cals': r['cals'],
+ 'sugar': r['sugar']
+ }]
+ day_rows = []
+ for date in reversed(sorted(db.days.keys())):
day = db.days[date]
- day_rows = "<tr>"\
- "<td><input name=\"day_date\" type=\"hidden\" value=\"%s\" />%s</td>"\
- "<td class=\"number\"><input name=\"day_cals\" type=\"hidden\" step=\"0.1\" min=\"0\" value=\"%.1f\" />%.1f</td>"\
- "<td class=\"number\"><input name=\"day_sugar\" type=\"hidden\" step=\"0.1\" min=\"0\" value=\"%.1f\" />%.1f</td>"\
- "</tr>" % (date, date[:10], day.calories, day.calories, day.sugar_g, day.sugar_g) + day_rows
- day_rows = "<tr>"\
- "<th>today:</th><th></th><th></th><th>archive?</th>"\
- "</tr>"\
- "<tr>"\
- "<td><input name=\"new_date\" size=8 value=\"%s\" /></td>"\
- "<td class=\"number\"><input name=\"new_day_cals\" type=\"hidden\" value=\"%.1f\" readonly />%.1f</td>"\
- "<td class=\"number\"><input name=\"new_day_sugar\" type=\"hidden\" value=\"%.1f\" readonly />%.1f</td>"\
- "<td><input name=\"archive_day\" type=\"checkbox\" /></td>"\
- "</tr>" % (db.today_date, db.today.calories, db.today.calories, db.today.sugar_g, db.today.sugar_g) + day_rows
- page = build_page(eatables, consumptions, db.eatables_selection(), day_rows)
- self.wfile.write(bytes(page, "utf-8"))
-
-
-if __name__ == "__main__":
- webServer = HTTPServer((hostName, serverPort), MyServer)
- print(f"Server started http://{hostName}:{serverPort}")
- try:
- webServer.serve_forever()
- except KeyboardInterrupt:
- pass
- webServer.server_close()
- print("Server stopped.")
+ day_rows += [{
+ 'date': date,
+ 'date_short': date[:10],
+ 'cals': f'{day.calories:.1f}',
+ 'sugar': f'{day.sugar_g:.1f}',
+ }]
+ page = jinja2.Template(tmpl).render(
+ db=db,
+ days=day_rows,
+ consumptions=consumption_rows,
+ eatables=eatable_rows,
+ eatables_selection=db.eatables_selection())
+ self.send_HTML(page)
+
+
+if __name__ == "__main__":
+ run_server(server_port, CaloriesServer)
-# from http.server import BaseHTTPRequestHandler, HTTPServer
import os
import json
import jinja2
from plomlib import PlomDB, PlomException, run_server, run_server, PlomServer
-server_port = 8081
+server_port = 8083
+db_path = '/home/plom/org/income.json'
-tmpl = jinja2.Template("""<html>
-<meta charset="UTF-8">
+tmpl = """
<style>
body {
font-family: monospace;
</style>
<body>
<table>
-<tr><th /><th>earned</th><th>progress</th><th>surplus</th></tr >
-{% for p in progress_bars %}
-<tr><th>{{p.title}}</th>
+<tr><th></th><th>earned</th><th>progress</th><th>surplus</th></tr >
+{% for p in progress_bars %}<tr><th>{{p.title}}</th>
<td class="countable">{{p.earned|round(2)}}</td>
-<td class="progressbar">{% if p.time_progress >= 0 %}<div class="time_progress" style="margin-left: {{p.time_progress}}px"></div>{% endif %}<div class="progress" style="background-color: {% if p.success < 0.5 %}red{% elif p.success < 1 %}yellow{% else %}green{% endif %}; width: {{p.success_income_cut}}"></div></td>
-<td class="progressbar surplusbar"><div class="diff_goal">{{p.diff_goal}}</div><div class="progressbar surplus" style="width: {{p.success_income_bonus}}" /></div></td></tr>
-{% endfor %}
-</table>
+<td class="progressbar">{% if p.time_progress >= 0 %}<div class="time_progress" style="margin-left: {{p.time_progress}}px"></div>{% endif %}<div class="progress" style="background-color: {% if p.success < 0.5 %}red{% elif p.success < 1 %}yellow{% else %}green{% endif %}; width: {{p.success_income_cut}}px"></div></td>
+<td class="progressbar surplusbar"><div class="diff_goal">{{p.diff_goal}}</div><div class="progressbar surplus" style="width: {{p.success_income_bonus}}px" ></div></td></tr>
+{% endfor %}</table>
+
<form action="/" method="POST">
<table>
<tr><th>hourly rate</th><th>worked today</th></tr>
<tr><td class="input_container"><input type="number" min="1" class="rate" name="workday_hourly_rate_1" value="{{workday_hourly_rate_1}}"/>€</td><td><input type="number" min="0" class="minutes" name="workday_minutes_worked_1" value="{{workday_minutes_worked_1}}" step="5" /> minutes</td>
<tr><td class="input_container"><input type="number" min="1" class="rate" name="workday_hourly_rate_2" value="{{workday_hourly_rate_2}}"/>€</td><td><input type="number" min="0" class="minutes" name="workday_minutes_worked_2" value="{{workday_minutes_worked_2}}" step="5" /> minutes</td>
<tr><td class="input_container"><input type="number" min="1" class="rate" name="workday_hourly_rate_3" value="{{workday_hourly_rate_3}}"/>€</td><td><input type="number" min="0" class="minutes" name="workday_minutes_worked_3" value="{{workday_minutes_worked_3}}" step="5" /> minutes</td>
+</table>
<table>
<tr><th>yearly income goal</th><td><input type="number" class="year_goal" min="1" name="year_goal" value="{{year_goal}}" />€</td></tr>
<tr><th>monthly income goal</th><td class="countable">{{month_goal|round(2)}}€</td></tr>
<input type="submit" name="update" value="update inputs" />
<input type="submit" name="finish" value="finish day" />
</form>
-</body
-</html>""")
+"""
+
class IncomeDB(PlomDB):
self.workday_minutes_worked_3 = 0,
self.year_goal = 20000,
self.workdays_per_month = 16
- super().__init__('_income')
+ super().__init__(db_path)
def read_db_file(self, f):
d = json.load(f)
def write_db(self):
self.write_text_to_db(json.dumps(self.to_dict()))
+
class ProgressBar:
def __init__(self, title, earned, goal, time_progress=-1):
self.title = title
if time_progress > 0:
self.success = success_income / time_progress
-# class MyServer(BaseHTTPRequestHandler):
+
class IncomeServer(PlomServer):
def do_POST(self):
db.workday_minutes_worked_2 = 0
db.workday_minutes_worked_3 = 0
db.write_db()
- self.send_response(302)
- self.send_header('Location', '/')
- self.end_headers()
+ self.redirect()
except PlomException as e:
self.fail_400(e)
ProgressBar("month", month_plus, month_goal, progress_time_month),
ProgressBar("week", week_plus, week_goal, progress_time_week),
ProgressBar("workday", day_income, workday_goal)]
- page = tmpl.render(
+ page = jinja2.Template(tmpl).render(
progress_bars = progress_bars,
workday_hourly_rate_1 = db.workday_hourly_rate_1,
workday_minutes_worked_1 = db.workday_minutes_worked_1,
except PlomException as e:
self.fail_400(e)
+
if __name__ == "__main__":
run_server(server_port, IncomeServer)
-from http.server import BaseHTTPRequestHandler, HTTPServer
import os
-import html
import jinja2
import decimal
from datetime import datetime, timedelta
from plomlib import PlomDB, PlomException, run_server, run_server, PlomServer
server_port = 8082
+db_path = '/home/plom/org/ledger2023.dat'
+
+html_head = """
+<style>
+body { color: #000000; }
+table { margin-bottom: 2em; }
+th, td { text-align: left }
+input[type=number] { text-align: right; font-family: monospace; }
+.money { font-family: monospace; text-align: right; }
+.comment { font-style: italic; color: #777777; }
+.meta { font-size: 0.75em; color: #777777; }
+.full_line_comment { display: block; white-space: nowrap; width: 0; }
+</style>
+<body>
+<a href="/">ledger</a>
+<a href="/balance">balance</a>
+<a href="/add_free">add free</a>
+<a href="/add_structured">add structured</a>
+<hr />
+"""
+booking_html = """
+<p id="{{nth}}"><a href="#{{nth}}">{{date}}</a> {{desc}} <span class="comment">{{head_comment|e}}</span><br />
+<span class="meta">[edit: <a href="/add_structured?start={{start}}&end={{end}}">structured</a>
+/ <a href="/add_free?start={{start}}&end={{end}}">free</a>
+| copy:<a href="/copy_structured?start={{start}}&end={{end}}">structured</a>
+/ <a href="/copy_free?start={{start}}&end={{end}}">free</a>
+| move {% if move_up %}<a href="/move_up?start={{start}}&end={{end}}">up</a>{% else %}up{% endif %}/{% if move_down %}<a href="/move_down?start={{start}}&end={{end}}">down</a>{% else %}down{% endif %}
+| <a href="/balance?stop={{nth+1}}">balance after</a>
+]</span>
+<table>
+{% for l in booking_lines %}
+{% if l.acc %}
+<tr><td>{{l.acc|e}}</td><td class="money">{{l.money|e}}</td><td class="comment">{{l.comment|e}}</td></tr>
+{% else %}
+<tr><td><div class="comment full_line_comment">{{l.comment|e}}</div></td></tr>
+{% endif %}
+{% endfor %}
+</table></p>
+"""
+add_form_header = """<form method="POST" action="{{action|e}}">
+<input type="submit" name="check" value="check" />
+<input type="submit" name="revert" value="revert" />
+"""
+add_form_footer = """
+<input type="hidden" name="start" value={{start}} />
+<input type="hidden" name="end" value={{end}} />
+<input type="submit" name="save" value="save!">
+</form>
+"""
+add_free_html = """<br />
+<textarea name="booking" rows=10 cols=80>
+{% for line in lines %}{{ line }}
+{% endfor %}
+</textarea>
+"""
+add_structured_html = """
+<input type="submit" name="add_taxes" value="add taxes" />
+<input type="submit" name="add_taxes2" value="add taxes2" />
+<input type="submit" name="add_sink" value="add sink" />
+<br />
+<input name="date" value="{{date|e}}" size=9 />
+<input name="description" value="{{desc|e}}" list="descriptions" />
+<textarea name="line_0_comment" rows=1 cols=20>{{head_comment|e}}</textarea>
+<input type="submit" name="line_0_add" value="[+]" />
+<br />
+{% for line in booking_lines %}
+<input name="line_{{line.i}}_account" value="{{line.acc|e}}" size=40 list="accounts" />
+<input type="number" name="line_{{line.i}}_amount" step=0.01 value="{{line.amt}}" size=10 />
+<input name="line_{{line.i}}_currency" value="{{line.curr|e}}" size=3 list="currencies" />
+<input type="submit" name="line_{{line.i}}_delete" value="[x]" />
+<input type="submit" name="line_{{line.i}}_delete_after" value="[XX]" />
+<input type="submit" name="line_{{line.i}}_add" value="[+]" />
+<textarea name="line_{{line.i}}_comment" rows=1 cols={% if line.comm_cols %}{{line.comm_cols}}{% else %}20{% endif %}>{{line.comment|e}}</textarea>
+<br />
+{% endfor %}
+{% for name, items in datalist_sets.items() %}
+<datalist id="{{name}}">
+{% for item in items %}
+ <option value="{{item|e}}">{{item|e}}</option>
+{% endfor %}
+</datalist>
+{% endfor %}
+"""
def apply_booking_to_account_balances(account_sums, account, currency, amount):
self.bookings = []
self.comments = []
self.real_lines = []
- super().__init__('_ledger')
+ super().__init__(db_path)
ret = parse_lines(self.real_lines)
self.bookings += ret[0]
self.comments += ret[1]
class LedgerServer(PlomServer):
- header = """<html>
-<meta charset="UTF-8">
-<style>
-body { color: #000000; }
-table { margin-bottom: 2em; }
-th, td { text-align: left }
-input[type=number] { text-align: right; font-family: monospace; }
-.money { font-family: monospace; text-align: right; }
-.comment { font-style: italic; color: #777777; }
-.meta { font-size: 0.75em; color: #777777; }
-.full_line_comment { display: block; white-space: nowrap; width: 0; }
-</style>
-<body>
-<a href="/">ledger</a>
-<a href="/balance">balance</a>
-<a href="/add_free">add free</a>
-<a href="/add_structured">add structured</a>
-<hr />
-"""
- booking_tmpl = jinja2.Template("""
-<p id="{{nth}}"><a href="#{{nth}}">{{date}}</a> {{desc}} <span class="comment">{{head_comment|e}}</span><br />
-<span class="meta">[edit: <a href="/add_structured?start={{start}}&end={{end}}">structured</a>
-/ <a href="/add_free?start={{start}}&end={{end}}">free</a>
-| copy:<a href="/copy_structured?start={{start}}&end={{end}}">structured</a>
-/ <a href="/copy_free?start={{start}}&end={{end}}">free</a>
-| move {% if move_up %}<a href="/move_up?start={{start}}&end={{end}}">up</a>{% else %}up{% endif %}/{% if move_down %}<a href="/move_down?start={{start}}&end={{end}}">down</a>{% else %}down{% endif %}
-| <a href="/balance?stop={{nth+1}}">balance after</a>
-]</span>
-<table>
-{% for l in booking_lines %}
-{% if l.acc %}
-<tr><td>{{l.acc|e}}</td><td class="money">{{l.money|e}}</td><td class="comment">{{l.comment|e}}</td></tr>
-{% else %}
-<tr><td><div class="comment full_line_comment">{{l.comment|e}}</div></td></tr>
-{% endif %}
-{% endfor %}
-</table></p>
-""")
- add_form_header = """<form method="POST" action="{{action|e}}">
-<input type="submit" name="check" value="check" />
-<input type="submit" name="revert" value="revert" />
-"""
- add_form_footer = """
-<input type="hidden" name="start" value={{start}} />
-<input type="hidden" name="end" value={{end}} />
-<input type="submit" name="save" value="save!">
-</form>
-"""
- footer = "</body>\n<html>"
+
+ def pre_init(self):
+ self.html_head += [html_head]
def do_POST(self):
try:
nth = db.get_nth_for_booking_of_start_line(new_start)
if new_start > start:
nth -= 1
- redir_url = f'/#{nth}'
- self.send_code_and_headers(302, [('Location', redir_url)])
+ self.redirect( f'/#{nth}')
# otherwise just re-build editing form
else:
if '/add_structured' == parsed_url.path:
edit_content = self.add_structured(db, start, end, temp_lines=lines, add_empty_line=add_empty_line)
else:
edit_content = self.add_free(db, start, end)
- self.send_HTML(self.header + edit_content + self.footer)
+ self.send_HTML(edit_content)
except PlomException as e:
self.fail_400(e)
start = int(params.get('start', ['0'])[0])
end = int(params.get('end', ['0'])[0])
db = LedgerDB()
- page = self.header
if parsed_url.path == '/balance':
stop = params.get('stop', [None])[0]
- page += self.balance_as_html(db, stop)
+ page = self.balance_as_html(db, stop)
elif parsed_url.path == '/add_free':
- page += self.add_free(db, start, end)
+ page = self.add_free(db, start, end)
elif parsed_url.path == '/add_structured':
- page += self.add_structured(db, start, end)
+ page = self.add_structured(db, start, end)
elif parsed_url.path == '/copy_free':
- page += self.add_free(db, start, end, copy=True)
+ page = self.add_free(db, start, end, copy=True)
elif parsed_url.path == '/copy_structured':
- page += self.add_structured(db, start, end, copy=True)
+ page = self.add_structured(db, start, end, copy=True)
elif parsed_url.path == '/move_up':
nth = self.move_up(db, start, end)
- self.send_code_and_headers(302, [('Location', f'/#{nth}')])
+ self.redirect(f'/#{nth}')
return
elif parsed_url.path == '/move_down':
nth = self.move_down(db, start, end)
- self.send_code_and_headers(302, [('Location', f'/#{nth}')])
+ self.redirect(f'/#{nth}')
return
else:
- page += self.ledger_as_html(db)
- page += self.footer
+ page = self.ledger_as_html(db)
self.send_HTML(page)
except PlomException as e:
self.fail_400(e)
return f"<pre>{content}</pre>"
def ledger_as_html(self, db):
+ booking_tmpl = jinja2.Template(booking_html)
single_c_tmpl = jinja2.Template('<span class="comment">{{c|e}}</span><br />') ##
elements_to_write = []
last_i = i = 0 ##
if booking_line[1] is not None:
money = f'{booking_line[1]} {booking_line[2]}'
booking_lines += [{'acc': booking_line[0], 'money':money, 'comment':comment}] ##
- elements_to_write += [self.booking_tmpl.render(
+ elements_to_write += [booking_tmpl.render(
nth=nth,
start=booking.start_line,
end=booking_end,
return '\n'.join(elements_to_write)
def add_free(self, db, start=0, end=0, copy=False):
- tmpl = jinja2.Template(self.add_form_header + """<br />
-<textarea name="booking" rows=10 cols=80>
-{% for line in lines %}{{ line }}
-{% endfor %}
-</textarea>
-""" + self.add_form_footer)
+ tmpl = jinja2.Template(add_form_header + add_free_html + add_form_footer)
lines = db.get_lines(start, end)
if copy:
start = end = 0
return tmpl.render(action='add_free', start=start, end=end, lines=lines)
def add_structured(self, db, start=0, end=0, copy=False, temp_lines=[], add_empty_line=None):
- tmpl = jinja2.Template(self.add_form_header + """
-<input type="submit" name="add_taxes" value="add taxes" />
-<input type="submit" name="add_taxes2" value="add taxes2" />
-<input type="submit" name="add_sink" value="add sink" />
-<br />
-<input name="date" value="{{date|e}}" size=9 />
-<input name="description" value="{{desc|e}}" list="descriptions" />
-<textarea name="line_0_comment" rows=1 cols=20>{{head_comment|e}}</textarea>
-<input type="submit" name="line_0_add" value="[+]" />
-<br />
-{% for line in booking_lines %}
-<input name="line_{{line.i}}_account" value="{{line.acc|e}}" size=40 list="accounts" />
-<input type="number" name="line_{{line.i}}_amount" step=0.01 value="{{line.amt}}" size=10 />
-<input name="line_{{line.i}}_currency" value="{{line.curr|e}}" size=3 list="currencies" />
-<input type="submit" name="line_{{line.i}}_delete" value="[x]" />
-<input type="submit" name="line_{{line.i}}_delete_after" value="[XX]" />
-<input type="submit" name="line_{{line.i}}_add" value="[+]" />
-<textarea name="line_{{line.i}}_comment" rows=1 cols={% if line.comm_cols %}{{line.comm_cols}}{% else %}20{% endif %}>{{line.comment|e}}</textarea>
-<br />
-{% endfor %}
-{% for name, items in datalist_sets.items() %}
-<datalist id="{{name}}">
-{% for item in items %}
- <option value="{{item|e}}">{{item|e}}</option>
-{% endfor %}
-</datalist>
-{% endfor %}
-""" + self.add_form_footer)
+ tmpl = jinja2.Template(add_form_header + add_structured_html + add_form_footer)
lines = temp_lines if len(''.join(temp_lines)) > 0 else db.get_lines(start, end)
bookings, comments = parse_lines(lines, validate_bookings=False)
if len(bookings) > 1:
class PlomDB:
def __init__(self, db_name):
- self.db_file = db_name + ".json"
- self.lock_file = db_name+ ".lock"
+ self.db_file = db_name
+ self.lock_file = db_name+ '.lock'
if os.path.exists(self.db_file):
- with open(self.db_file, "r") as f:
+ with open(self.db_file, 'r') as f:
self.read_db_file(f)
def lock(self):
class PlomServer(BaseHTTPRequestHandler):
- header = ''
- footer = ''
-
- def run(self, port):
- from http.server import HTTPServer
- webServer = HTTPServer(('localhost', port), type(self))
- print(f"Server started http://localhost:{port}")
- try:
- webServer.serve_forever()
- except KeyboardInterrupt:
- pass
- webServer.server_close()
- print("Server stopped.")
+
+ def __init__(self, *args, **kwargs):
+ self.html_head = ['<!DOCTYPE html>\n<html>\n<meta charset="UTF-8">']
+ self.html_foot = ['</body>\n</html>']
+ self.pre_init()
+ super().__init__(*args, **kwargs)
+
+ def pre_init(self):
+ pass
def fail_400(self, e):
- page = f'{self.header}ERROR: {e}{self.footer}'
- self.send_HTML(page, 400)
+ self.send_HTML(f'ERROR: {e}', 400)
def send_HTML(self, html, code=200):
self.send_code_and_headers(code, [('Content-type', 'text/html')])
- self.wfile.write(bytes(html, "utf-8"))
+ header = '\n'.join(self.html_head)
+ footer = '\n'.join(self.html_foot)
+ self.wfile.write(bytes(f'{header}\n{html}\n{footer}', 'utf-8'))
def send_code_and_headers(self, code, headers=[]):
self.send_response(code)
self.send_header(fieldname, content)
self.end_headers()
+ def redirect(self, url='/'):
+ self.send_code_and_headers(302, [('Location', url)])
+
+
def run_server(port, server_class):
from http.server import HTTPServer