4 from plomlib import PlomDB, PlomException, run_server, PlomHandler
7 db_path = '/home/plom/org/income.json'
12 font-family: monospace;
13 background-color: #e0e0ff;
16 font-family: monospace;
20 border: 1px solid black;
25 background-color: black;
34 background-color: white;
36 border-left: 1px solid black;
37 border-right: 1px solid black;
45 background-color: green;
48 font-family: monospace;
80 <tr><th></th><th>earned</th><th>progress</th><th>surplus</th></tr >
81 {% for p in progress_bars %}<tr><th>{{p.title}}</th>
82 <td class="countable">{{p.earned|round(2)}}</td>
83 <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>
84 <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>
87 <form action="{{homepage}}" method="POST">
89 <tr><th>hourly rate</th><th>worked today</th></tr>
90 <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>
91 <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>
92 <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>
95 <tr><th>yearly income goal</th><td><input type="number" class="year_goal" min="1" name="year_goal" value="{{year_goal}}" />€</td></tr>
96 <tr><th>monthly income goal</th><td class="countable">{{month_goal|round(2)}}€</td></tr>
97 <tr><th>weekly income goal</th><td class="countable">{{week_goal|round(2)}}€</td></tr>
98 <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>
99 <tr><th>workday income goal</th><td class="countable">{{workday_goal|round(2)}}€</td></tr>
100 <tr><th>workdays per week</th><td class="countable">{{workdays_per_week|round(2)}}</td></tr>
102 <input type="submit" name="update" value="update inputs" />
103 <input type="submit" name="finish" value="finish day" />
108 class IncomeDB(PlomDB):
112 self.timestamp_year = 0,
113 self.timestamp_month = 0,
114 self.timestamp_week = 0,
115 self.year_income = 0,
116 self.month_income = 0,
117 self.week_income = 0,
118 self.workday_hourly_rate_1 = 10,
119 self.workday_hourly_rate_2 = 25,
120 self.workday_hourly_rate_3 = 50,
121 self.workday_minutes_worked_1 = 0,
122 self.workday_minutes_worked_2 = 0,
123 self.workday_minutes_worked_3 = 0,
124 self.year_goal = 20000,
125 self.workdays_per_month = 16
126 super().__init__(db_path)
128 def read_db_file(self, f):
130 for k, v in d.items():
131 if not hasattr(self, k):
132 raise PlomException("bad key in db: " + k)
136 keys = [k for k in dir(self) if (not k.startswith('_')) and (not callable(getattr(self, k)))]
139 d[k] = getattr(self, k)
143 self.write_text_to_db(json.dumps(self.to_dict()))
147 def __init__(self, title, earned, goal, time_progress=-1):
150 self.time_progress = int(time_progress * 100)
151 success_income = self.earned / goal
152 self.success_income_cut = int(min(success_income, 1.0) * 100)
153 self.success_income_bonus = int(max(success_income - 1.0, 0) * 100)
154 self.success = success_income + 0
155 self.diff_goal = "%.2f€" % (self.earned - goal)
156 if title != "workday":
157 self.diff_goal += "(%.2f€)" % (self.earned - (goal * time_progress))
158 if time_progress >= 0:
160 if time_progress > 0:
161 self.success = success_income / time_progress
164 class IncomeProgressHandler(PlomHandler):
166 def app_init(self, handler):
167 default_path = '/income_progress'
168 handler.add_route('GET', default_path, self.display_income_progress)
169 handler.add_route('POST', default_path, self.post_income_update)
170 return 'income_progress', default_path
173 self.try_do(self.post_income_update)
175 def post_income_update(self):
176 from urllib.parse import parse_qs
177 length = int(self.headers['content-length'])
178 postvars = parse_qs(self.rfile.read(length).decode(), keep_blank_values=1)
180 db.workday_minutes_worked_1 = int(postvars['workday_minutes_worked_1'][0])
181 db.workday_minutes_worked_2 = int(postvars['workday_minutes_worked_2'][0])
182 db.workday_minutes_worked_3 = int(postvars['workday_minutes_worked_3'][0])
183 db.workday_hourly_rate_1 = int(postvars['workday_hourly_rate_1'][0])
184 db.workday_hourly_rate_2 = int(postvars['workday_hourly_rate_2'][0])
185 db.workday_hourly_rate_3 = int(postvars['workday_hourly_rate_3'][0])
186 db.year_goal = int(postvars['year_goal'][0])
187 db.workdays_per_month = int(postvars['workdays_per_month'][0])
188 if 'finish' in postvars.keys():
189 day_income = (db.workday_minutes_worked_1 / 60.0) * db.workday_hourly_rate_1
190 day_income += (db.workday_minutes_worked_2 / 60.0) * db.workday_hourly_rate_2
191 day_income += (db.workday_minutes_worked_3 / 60.0) * db.workday_hourly_rate_3
192 db.year_income += day_income
193 db.month_income += day_income
194 db.week_income += day_income
195 db.workday_minutes_worked_1 = 0
196 db.workday_minutes_worked_2 = 0
197 db.workday_minutes_worked_3 = 0
199 homepage = self.apps['income_progress'] if hasattr(self, 'apps') else self.homepage
200 self.redirect(homepage)
203 self.try_do(self.display_income_progress)
205 def display_income_progress(self):
209 today = datetime.datetime.now()
211 if today.year != db.timestamp_year:
212 db.timestamp_year = today.year
213 db.timestamp_month = today.month
217 if today.month != db.timestamp_month:
218 db.timestamp_month = today.month
221 if today.isocalendar()[1] != db.timestamp_week:
222 db.timestamp_week = today.isocalendar()[1]
226 print("Resetting timestamp")
228 day_of_year = today.toordinal() - datetime.date(today.year, 1, 1).toordinal() + 1
229 year_length = 365 + calendar.isleap(today.year)
230 workday_goal = db.year_goal / 12 / db.workdays_per_month
231 workdays_per_week = (db.workdays_per_month * 12) / (year_length / 7)
232 month_goal = db.year_goal / 12
233 week_goal = db.year_goal / (year_length / 7)
234 day_income = (db.workday_minutes_worked_1 / 60.0) * db.workday_hourly_rate_1
235 day_income += (db.workday_minutes_worked_2 / 60.0) * db.workday_hourly_rate_2
236 day_income += (db.workday_minutes_worked_3 / 60.0) * db.workday_hourly_rate_3
237 year_plus = db.year_income + day_income
238 month_plus = db.month_income + day_income
239 week_plus = db.week_income + day_income
240 progress_time_year = day_of_year / year_length
241 progress_time_month = today.day / calendar.monthrange(today.year, today.month)[1]
242 progress_time_week = today.weekday() / 7
243 progress_bars = [ProgressBar("year", year_plus, db.year_goal, progress_time_year),
244 ProgressBar("month", month_plus, month_goal, progress_time_month),
245 ProgressBar("week", week_plus, week_goal, progress_time_week),
246 ProgressBar("workday", day_income, workday_goal)]
247 homepage = self.apps['income_progress'] if hasattr(self, 'apps') else self.homepage
248 page = jinja2.Template(tmpl).render(
250 progress_bars = progress_bars,
251 workday_hourly_rate_1 = db.workday_hourly_rate_1,
252 workday_minutes_worked_1 = db.workday_minutes_worked_1,
253 workday_hourly_rate_2 = db.workday_hourly_rate_2,
254 workday_minutes_worked_2 = db.workday_minutes_worked_2,
255 workday_hourly_rate_3 = db.workday_hourly_rate_3,
256 workday_minutes_worked_3 = db.workday_minutes_worked_3,
257 year_goal = db.year_goal,
258 month_goal = month_goal,
259 week_goal = week_goal,
260 workdays_per_month = db.workdays_per_month,
261 workday_goal = workday_goal,
262 workdays_per_week = workdays_per_week,
267 if __name__ == "__main__":
268 run_server(server_port, IncomeProgressHandler)