From: Christian Heller <c.heller@plomlompom.de> Date: Tue, 7 Nov 2023 05:37:33 +0000 (+0100) Subject: Improve income_progress_bars.py. X-Git-Url: https://plomlompom.com/repos/%7B%7Bprefix%7D%7D/static/%7B%7Bdb.prefix%7D%7D/day_todos?a=commitdiff_plain;h=24835e99387f4df91280ad83208f6db3e10ecea5;p=misc Improve income_progress_bars.py. --- diff --git a/income_progress_bars.py b/income_progress_bars.py index 1ecd8b9..2093a24 100644 --- a/income_progress_bars.py +++ b/income_progress_bars.py @@ -1,11 +1,12 @@ 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 { @@ -33,8 +34,8 @@ td, th { 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 { @@ -78,27 +79,32 @@ table { <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, @@ -114,7 +120,7 @@ db_default = { "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" @@ -125,137 +131,156 @@ def load_db(): 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: