</body
</html>""")
-db_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
-}
-db_file = "db.json"
-lock_file = "db.lock"
-def load_db():
- if os.path.exists(db_file):
- with open(db_file, "r") as f:
- return json.load(f)
- else:
- return db_default
+class Database:
+ timestamp_year = 0,
+ timestamp_month = 0,
+ timestamp_week = 0,
+ year_income = 0,
+ month_income = 0,
+ week_income = 0,
+ workday_hourly_rate_1 = 10,
+ workday_hourly_rate_2 = 25,
+ workday_hourly_rate_3 = 50,
+ workday_minutes_worked_1 = 0,
+ workday_minutes_worked_2 = 0,
+ workday_minutes_worked_3 = 0,
+ year_goal = 20000,
+ workdays_per_month = 16
+
+ def __init__(self):
+ db_name = "_income"
+ self.db_file = db_name + ".json"
+ self.lock_file = db_name+ ".lock"
+ if os.path.exists(self.db_file):
+ with open(self.db_file, "r") as f:
+ d = json.load(f)
+ for k, v in d.items():
+ if not hasattr(self, k):
+ raise Exception("bad key in db: " + k)
+ setattr(self, k, v)
+
+ def lock(self):
+ if os.path.exists(self.lock_file):
+ raise Exception('Sorry, lock file!')
+ f = open(self.lock_file, 'w+')
+ f.close()
+
+ def unlock(self):
+ os.remove(self.lock_file)
+
+ def to_dict(self):
+ keys = [k for k in dir(self) if (not k.startswith('_')) and (not callable(getattr(self, k)))]
+ d = {}
+ for k in keys:
+ d[k] = getattr(self, k)
+ return d
+
+ def write_db(self):
+ self.write_text_to_db(json.dumps(self.to_dict()))
+
+ def backup(self):
+ import shutil
+ from datetime import datetime, timedelta
+ # collect modification times of numbered .bak files
+ bak_prefix = f'{self.db_file}.bak.'
+ backup_dates = []
+ i = 0
+ bak_as = f'{bak_prefix}{i}'
+ while os.path.exists(bak_as):
+ mod_time = os.path.getmtime(bak_as)
+ backup_dates += [str(datetime.fromtimestamp(mod_time))]
+ i += 1
+ bak_as = f'{bak_prefix}{i}'
+
+ # collect what numbered .bak files to save: the older, the fewer; for each
+ # timedelta, keep the newest file that's older
+ ages_to_keep = [timedelta(minutes=4**i) for i in range(0, 8)]
+ now = datetime.now()
+ to_save = []
+ for age in ages_to_keep:
+ limit = now - age
+ for i, date in enumerate(reversed(backup_dates)):
+ if datetime.strptime(date, '%Y-%m-%d %H:%M:%S.%f') < limit:
+ unreversed_i = len(backup_dates) - i - 1
+ if unreversed_i not in to_save:
+ to_save += [unreversed_i]
+ break
+
+ # remove redundant backup files
+ j = 0
+ for i in to_save:
+ if i != j:
+ source = f'{bak_prefix}{i}'
+ target = f'{bak_prefix}{j}'
+ shutil.move(source, target)
+ j += 1
+ for i in range(j, len(backup_dates)):
+ try:
+ os.remove(f'{bak_prefix}{i}')
+ except FileNotFoundError:
+ pass
+
+ # put copy of current state at end of bak list
+ shutil.copy(self.db_file, f'{bak_prefix}{j}')
-# 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
+ def write_text_to_db(self, text):
+ self.lock()
+ self.backup()
+ with open(self.db_file, 'w') as f:
+ f.write(text);
+ self.unlock()
class ProgressBar:
def __init__(self, title, earned, goal, time_progress=-1):
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 = load_db()
- 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])
+ db = Database()
+ 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 '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
- db["workday_minutes_worked_1"] = 0
- db["workday_minutes_worked_2"] = 0
- db["workday_minutes_worked_3"] = 0
- 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)
+ 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
+ db.write_db()
self.send_response(302)
self.send_header('Location', '/')
self.end_headers()
def do_GET(self):
import datetime
import calendar
- db = load_db()
+ db = Database() #load_db()
today = datetime.datetime.now()
update_db = False
- if today.year != db["timestamp_year"]:
- db["timestamp_year"] = today.year
- db["year_income"] = 0
+ if today.year != db.timestamp_year:
+ db.timestamp_year = today.year
+ db.timestamp_month = today.month
+ db.year_income = 0
+ db.month_income = 0
update_db = True
- if today.month != db["timestamp_month"]:
- db["timestamp_month"] = today.month
- db["month_income"] = 0
+ 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
+ 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)
+ db.write_db()
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)
- 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
+ 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
- progress_bars = [ProgressBar("year", year_plus, db["year_goal"], progress_time_year),
+ 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,
+ 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")