from http.server import BaseHTTPRequestHandler, HTTPServer
import os
import json
+import jinja2
hostName = "localhost"
serverPort = 8081
-header = """<html>
+tmpl = jinja2.Template("""<html>
<meta charset="UTF-8">
<style>
body {
height: 20px;
background-color: white;
width: 2px;
- border-left: 1px solid black;
- border-right: 1px solid black;
+ border-left: 1px solid black;
+ border-right: 1px solid black;
z-index: 2;
}
.progress {
<body>
<table>
<tr><th /><th>earned</th><th>progress</th><th>surplus</th></tr >
-"""
-footer = """</table>
+{% 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>
<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="%s"/>€</td><td><input type="number" min="0" class="minutes" name="workday_minutes_worked_1" value="%s" step="5" /> minutes</td>
-<tr><td class="input_container"><input type="number" min="1" class="rate" name="workday_hourly_rate_2" value="%s"/>€</td><td><input type="number" min="0" class="minutes" name="workday_minutes_worked_2" value="%s" step="5" /> minutes</td>
-<tr><td class="input_container"><input type="number" min="1" class="rate" name="workday_hourly_rate_3" value="%s"/>€</td><td><input type="number" min="0" class="minutes" name="workday_minutes_worked_3" value="%s" step="5" /> minutes</td>
+<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>
-<tr><th>yearly income goal</th><td><input type="number" class="year_goal" min="1" name="year_goal" value="%s" />€</td></tr>
-<tr><th>monthly income goal</th><td class="countable">%.2f€</td></tr>
-<tr><th>weekly income goal</th><td class="countable">%.2f€</td></tr>
-<tr><th>workdays per month</th><td class="input_container"><input type="number" class="workdays" min="1" max="28" name="workdays_per_month" value="%s" /></td></tr>
-<tr><th>workday income goal</th><td class="countable">%.2f€</td></tr>
-<tr><th>workdays per week</th><td class="countable">%.2f€</td></tr>
+<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>
+<tr><th>weekly income goal</th><td class="countable">{{week_goal|round(2)}}€</td></tr>
+<tr><th>workdays per month</th><td class="input_container"><input type="number" class="workdays" min="1" max="28" name="workdays_per_month" value="{{workdays_per_month}}" /></td></tr>
+<tr><th>workday income goal</th><td class="countable">{{workday_goal|round(2)}}€</td></tr>
+<tr><th>workdays per week</th><td class="countable">{{workdays_per_week|round(2)}}</td></tr>
</table>
<input type="submit" name="update" value="update inputs" />
<input type="submit" name="finish" value="finish day" />
</form>
</body
-</html>"""
+</html>""")
db_default = {
"timestamp_year": 0,
"workday_hourly_rate_3": 50,
"workday_minutes_worked_3": 0,
"year_goal": 20000,
- "workdays_per_month": 16
+ "workdays_per_month": 16
}
db_file = "db.json"
lock_file = "db.lock"
else:
return db_default
+# class Database:
+# data_default = {
+# "timestamp_year": 0,
+# "timestamp_month": 0,
+# "timestamp_week": 0,
+# "year_income": 0,
+# "month_income": 0,
+# "week_income": 0,
+# "workday_hourly_rate_1": 10,
+# "workday_minutes_worked_1": 0,
+# "workday_hourly_rate_2": 25,
+# "workday_minutes_worked_2": 0,
+# "workday_hourly_rate_3": 50,
+# "workday_minutes_worked_3": 0,
+# "year_goal": 20000,
+# "workdays_per_month": 16
+# }
+# def __init__(self):
+# if os.path.exists(db_file):
+# with open(db_file, "r") as f:
+# return json.load(f)
+# else:
+# return db_default
+
+class ProgressBar:
+ def __init__(self, title, earned, goal, time_progress=-1):
+ self.title = title
+ self.earned = earned
+ self.time_progress = int(time_progress * 100)
+ success_income = self.earned / goal
+ self.success_income_cut = int(min(success_income, 1.0) * 100)
+ self.success_income_bonus = int(max(success_income - 1.0, 0) * 100)
+ self.success = success_income + 0
+ self.diff_goal = "%.2f€" % (self.earned - goal)
+ if title != "workday":
+ self.diff_goal += "(%.2f€)" % (self.earned - (goal * time_progress))
+ if time_progress >= 0:
+ self.success = 1
+ if time_progress > 0:
+ self.success = success_income / time_progress
+
class MyServer(BaseHTTPRequestHandler):
def do_POST(self):
from urllib.parse import parse_qs
length = int(self.headers['content-length'])
- postvars = parse_qs(self.rfile.read(length), keep_blank_values=1)
+ postvars = parse_qs(self.rfile.read(length).decode(), keep_blank_values=1)
db = load_db()
- db["workday_minutes_worked_1"] = int(postvars[b'workday_minutes_worked_1'][0].decode())
- db["workday_minutes_worked_2"] = int(postvars[b'workday_minutes_worked_2'][0].decode())
- db["workday_minutes_worked_3"] = int(postvars[b'workday_minutes_worked_3'][0].decode())
- db["workday_hourly_rate_1"] = int(postvars[b'workday_hourly_rate_1'][0].decode())
- db["workday_hourly_rate_2"] = int(postvars[b'workday_hourly_rate_2'][0].decode())
- db["workday_hourly_rate_3"] = int(postvars[b'workday_hourly_rate_3'][0].decode())
- db["year_goal"] = int(postvars[b'year_goal'][0].decode())
- db["workdays_per_month"] = int(postvars[b'workdays_per_month'][0].decode())
+ db["workday_minutes_worked_1"] = int(postvars['workday_minutes_worked_1'][0])
+ db["workday_minutes_worked_2"] = int(postvars['workday_minutes_worked_2'][0])
+ db["workday_minutes_worked_3"] = int(postvars['workday_minutes_worked_3'][0])
+ db["workday_hourly_rate_1"] = int(postvars['workday_hourly_rate_1'][0])
+ db["workday_hourly_rate_2"] = int(postvars['workday_hourly_rate_2'][0])
+ db["workday_hourly_rate_3"] = int(postvars['workday_hourly_rate_3'][0])
+ db["year_goal"] = int(postvars['year_goal'][0])
+ db["workdays_per_month"] = int(postvars['workdays_per_month'][0])
if b'finish' in postvars.keys():
- day_income = (db["workday_minutes_worked_1"] / 60.0) * db["workday_hourly_rate_1"]
- day_income += (db["workday_minutes_worked_2"] / 60.0) * db["workday_hourly_rate_2"]
- day_income += (db["workday_minutes_worked_3"] / 60.0) * db["workday_hourly_rate_3"]
- db["year_income"] += day_income
- db["month_income"] += day_income
- db["week_income"] += day_income
+ day_income = (db["workday_minutes_worked_1"] / 60.0) * db["workday_hourly_rate_1"]
+ day_income += (db["workday_minutes_worked_2"] / 60.0) * db["workday_hourly_rate_2"]
+ day_income += (db["workday_minutes_worked_3"] / 60.0) * db["workday_hourly_rate_3"]
+ db["year_income"] += day_income
+ db["month_income"] += day_income
+ db["week_income"] += day_income
db["workday_minutes_worked_1"] = 0
db["workday_minutes_worked_2"] = 0
db["workday_minutes_worked_3"] = 0
- if not os.path.exists(lock_file):
- with open(lock_file, "w+"): pass
- with open(db_file, "w") as f:
- json.dump(db, f)
- os.remove(lock_file)
- self.send_response(302)
- self.send_header('Location', '/')
- self.end_headers()
- else:
- self.send_response(400)
- self.end_headers()
- self.wfile.write(bytes("Sorry, lock file!", "utf-8"))
+ if self.fail_on_lockfile():
+ return
+ with open(lock_file, "w+"): pass
+ with open(db_file, "w") as f:
+ json.dump(db, f)
+ os.remove(lock_file)
+ self.send_response(302)
+ self.send_header('Location', '/')
+ self.end_headers()
def do_GET(self):
import datetime
- import calendar
- self.send_response(200)
- self.send_header("Content-type", "text/html")
- self.end_headers()
+ import calendar
db = load_db()
today = datetime.datetime.now()
- if not os.path.exists(lock_file):
- update_db = False
- if today.year != db["timestamp_year"]:
- db["timestamp_year"] = today.year
- db["year_income"] = 0
- update_db = True
- if today.month != db["timestamp_month"]:
- db["timestamp_month"] = today.month
- db["month_income"] = 0
- update_db = True
- if today.isocalendar()[1] != db["timestamp_week"]:
- db["timestamp_week"] = today.isocalendar()[1]
- db["week_income"] = 0
- update_db = True
- if update_db:
- print("Resetting timestamp")
- with open(lock_file, "w+"): pass
- with open(db_file, "w") as f:
- json.dump(db, f)
- os.remove(lock_file)
- else:
- self.send_response(400)
- self.end_headers()
- self.wfile.write(bytes("Sorry, lock file!", "utf-8"))
+ update_db = False
+ if today.year != db["timestamp_year"]:
+ db["timestamp_year"] = today.year
+ db["year_income"] = 0
+ update_db = True
+ if today.month != db["timestamp_month"]:
+ db["timestamp_month"] = today.month
+ db["month_income"] = 0
+ update_db = True
+ if today.isocalendar()[1] != db["timestamp_week"]:
+ db["timestamp_week"] = today.isocalendar()[1]
+ db["week_income"] = 0
+ update_db = True
+ if self.fail_on_lockfile():
return
+ if update_db:
+ print("Resetting timestamp")
+ with open(lock_file, "w+"): pass
+ with open(db_file, "w") as f:
+ json.dump(db, f)
+ os.remove(lock_file)
day_of_year = today.toordinal() - datetime.date(today.year, 1, 1).toordinal() + 1
year_length = 365 + calendar.isleap(today.year)
- workday_goal = db["year_goal"] / 12 / db["workdays_per_month"]
- workdays_per_week = (db["workdays_per_month"] * 12) / (year_length / 7)
- month_goal = db["year_goal"] / 12
- week_goal = db["year_goal"] / (year_length / 7)
- def success_color(success):
- if success < 0.5:
- return "red";
- elif success < 1:
- return "yellow";
- else:
- return "green"
- def progressbar(title, earned, goal, time_progress=-1):
- time_progress_indicator = ""
- success_income = earned / goal
- success_income_cut = min(success_income, 1.0)
- success_income_bonus = max(success_income - 1.0, 0)
- success = success_income + 0
- diff_goal = "%.2f€" % (earned - goal)
- if title != "workday":
- diff_goal += "(%.2f€)" % (earned - (goal * time_progress))
- if time_progress >= 0:
- success = 1
- if time_progress > 0:
- success = success_income / time_progress
- time_progress_indicator = "<div class=\"time_progress\" style=\"margin-left: %spx\"></div>" % int(time_progress * 100)
- return "<tr><th>%s</th>" \
- "<td class=\"countable\">%.2f€</td>" \
- "<td class=\"progressbar\">%s<div class=\"progress\" style=\"background-color: %s; width: %s\"></div></td>" \
- "<td class=\"progressbar surplusbar\"><div class=\"diff_goal\">%s</div><div class=\"progress surplus\" style=\"width: %s\"></div></td></tr>" % (
- title, earned, time_progress_indicator, success_color(success), int(success_income_cut * 100), diff_goal, int(success_income_bonus * 100))
- day_income = (db["workday_minutes_worked_1"] / 60.0) * db["workday_hourly_rate_1"]
- day_income += (db["workday_minutes_worked_2"] / 60.0) * db["workday_hourly_rate_2"]
- day_income += (db["workday_minutes_worked_3"] / 60.0) * db["workday_hourly_rate_3"]
- year_plus = db["year_income"] + day_income
- month_plus = db["month_income"] + day_income
+ workday_goal = db["year_goal"] / 12 / db["workdays_per_month"]
+ workdays_per_week = (db["workdays_per_month"] * 12) / (year_length / 7)
+ month_goal = db["year_goal"] / 12
+ week_goal = db["year_goal"] / (year_length / 7)
+ day_income = (db["workday_minutes_worked_1"] / 60.0) * db["workday_hourly_rate_1"]
+ day_income += (db["workday_minutes_worked_2"] / 60.0) * db["workday_hourly_rate_2"]
+ day_income += (db["workday_minutes_worked_3"] / 60.0) * db["workday_hourly_rate_3"]
+ year_plus = db["year_income"] + day_income
+ month_plus = db["month_income"] + day_income
week_plus = db["week_income"] + day_income
- progress_time_year = day_of_year / year_length
- progress_time_month = today.day / calendar.monthrange(today.year, today.month)[1]
- progress_time_week = today.weekday() / 7
- year_line = progressbar("year", year_plus, db["year_goal"], progress_time_year)
- month_line = progressbar("month", month_plus, month_goal, progress_time_month)
- week_line = progressbar("week", week_plus, week_goal, progress_time_week)
- day_line = progressbar("workday", day_income, workday_goal)
- body = year_line + "\n" + month_line + "\n" + week_line + "\n" + day_line
- page = header + body + footer % (
- db["workday_hourly_rate_1"], db["workday_minutes_worked_1"],
- db["workday_hourly_rate_2"], db["workday_minutes_worked_2"],
- db["workday_hourly_rate_3"], db["workday_minutes_worked_3"],
- db["year_goal"],
- month_goal,
- week_goal,
- db["workdays_per_month"],
- workday_goal,
- workdays_per_week,
+ progress_time_year = day_of_year / year_length
+ progress_time_month = today.day / calendar.monthrange(today.year, today.month)[1]
+ progress_time_week = today.weekday() / 7
+ progress_bars = [ProgressBar("year", year_plus, db["year_goal"], progress_time_year),
+ 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(
+ progress_bars = progress_bars,
+ workday_hourly_rate_1=db["workday_hourly_rate_1"],
+ workday_minutes_worked_1=db["workday_minutes_worked_1"],
+ workday_hourly_rate_2=db["workday_hourly_rate_2"],
+ workday_minutes_worked_2=db["workday_minutes_worked_2"],
+ workday_hourly_rate_3=db["workday_hourly_rate_3"],
+ workday_minutes_worked_3=db["workday_minutes_worked_3"],
+ year_goal=db["year_goal"],
+ month_goal=month_goal,
+ week_goal=week_goal,
+ workdays_per_month=db["workdays_per_month"],
+ workday_goal=workday_goal,
+ workdays_per_week=workdays_per_week,
)
+ self.send_response(200)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
self.wfile.write(bytes(page, "utf-8"))
-if __name__ == "__main__":
+ def fail_on_lockfile(self):
+ if os.path.exists(lock_file):
+ self.send_response(400)
+ self.end_headers()
+ self.wfile.write(bytes("Sorry, lock file!", "utf-8"))
+ return True
+ return False
+
+if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print(f"Server started http://{hostName}:{serverPort}")
try: