summaryrefslogtreecommitdiff
path: root/overlays/worktime/worktime.py
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2022-09-13 10:29:35 +0200
committerGregor Kleen <gkleen@yggdrasil.li>2022-09-13 10:29:35 +0200
commitb931543508377c0e48a6801e4ea217eb523e2b03 (patch)
tree373c8ab46c6e78cb69654d816fadf8d6fef1fd28 /overlays/worktime/worktime.py
parent92dab2dbad09bee9698fc0a9734140af37ca550a (diff)
downloadnixos-b931543508377c0e48a6801e4ea217eb523e2b03.tar
nixos-b931543508377c0e48a6801e4ea217eb523e2b03.tar.gz
nixos-b931543508377c0e48a6801e4ea217eb523e2b03.tar.bz2
nixos-b931543508377c0e48a6801e4ea217eb523e2b03.tar.xz
nixos-b931543508377c0e48a6801e4ea217eb523e2b03.zip
...
Diffstat (limited to 'overlays/worktime/worktime.py')
-rwxr-xr-xoverlays/worktime/worktime.py58
1 files changed, 36 insertions, 22 deletions
diff --git a/overlays/worktime/worktime.py b/overlays/worktime/worktime.py
index 9cfc6cd4..1fc00061 100755
--- a/overlays/worktime/worktime.py
+++ b/overlays/worktime/worktime.py
@@ -117,6 +117,7 @@ class Worktime(object):
117 force_day_to_work = True 117 force_day_to_work = True
118 leave_days = set() 118 leave_days = set()
119 leave_budget = dict() 119 leave_budget = dict()
120 time_per_day = None
120 121
121 @staticmethod 122 @staticmethod
122 def holidays(year): 123 def holidays(year):
@@ -151,10 +152,10 @@ class Worktime(object):
151 def __init__(self, start_datetime=None, end_datetime=None, now=None, include_running=True, force_day_to_work=True, **kwargs): 152 def __init__(self, start_datetime=None, end_datetime=None, now=None, include_running=True, force_day_to_work=True, **kwargs):
152 self.include_running = include_running 153 self.include_running = include_running
153 self.force_day_to_work = force_day_to_work 154 self.force_day_to_work = force_day_to_work
154 155
155 if now: 156 if now:
156 self.now = now 157 self.now = now
157 158
158 config = Worktime.config() 159 config = Worktime.config()
159 config_dir = BaseDirectory.load_first_config('worktime') 160 config_dir = BaseDirectory.load_first_config('worktime')
160 api = TogglAPI(api_token=config['TOGGL']['ApiToken'], workspace_id=config['TOGGL']['Workspace']) 161 api = TogglAPI(api_token=config['TOGGL']['ApiToken'], workspace_id=config['TOGGL']['Workspace'])
@@ -174,17 +175,17 @@ class Worktime(object):
174 except IOError as e: 175 except IOError as e:
175 if e.errno != 2: 176 if e.errno != 2:
176 raise e 177 raise e
177 178
178 179
179 hours_per_week = float(config.get('WORKTIME', 'HoursPerWeek', fallback=40)) 180 hours_per_week = float(config.get('WORKTIME', 'HoursPerWeek', fallback=40))
180 workdays = set([int(d.strip()) for d in config.get('WORKTIME', 'Workdays', fallback='1,2,3,4,5').split(',')]) 181 workdays = set([int(d.strip()) for d in config.get('WORKTIME', 'Workdays', fallback='1,2,3,4,5').split(',')])
181 time_per_day = timedelta(hours = hours_per_week) / len(workdays) 182 self.time_per_day = timedelta(hours = hours_per_week) / len(workdays)
182 183
183 holidays = dict() 184 holidays = dict()
184 185
185 leave_per_year = int(config.get('WORKTIME', 'LeavePerYear', fallback=30)) 186 leave_per_year = int(config.get('WORKTIME', 'LeavePerYear', fallback=30))
186 for year in range(start_date.year, end_date.year + 1): 187 for year in range(start_date.year, end_date.year + 1):
187 holidays |= {k: v * time_per_day for k, v in Worktime.holidays(year).items()} 188 holidays |= {k: v * self.time_per_day for k, v in Worktime.holidays(year).items()}
188 leave_frac = 1 189 leave_frac = 1
189 if date(year, 1, 1) < start_date.date(): 190 if date(year, 1, 1) < start_date.date():
190 leave_frac = (date(year + 1, 1, 1) - start_date.date()) / (date(year + 1, 1, 1) - date(year, 1, 1)) 191 leave_frac = (date(year + 1, 1, 1) - start_date.date()) / (date(year + 1, 1, 1) - date(year, 1, 1))
@@ -199,7 +200,7 @@ class Worktime(object):
199 day = datetime.strptime(datestr, date_format).replace(tzinfo=tzlocal()).date() 200 day = datetime.strptime(datestr, date_format).replace(tzinfo=tzlocal()).date()
200 if day != start_date.date(): 201 if day != start_date.date():
201 continue 202 continue
202 203
203 self.leave_budget[day.year] = (self.leave_budget[day.year] if day.year in self.leave_budget else 0) + int(count) 204 self.leave_budget[day.year] = (self.leave_budget[day.year] if day.year in self.leave_budget else 0) + int(count)
204 except IOError as e: 205 except IOError as e:
205 if e.errno != 2: 206 if e.errno != 2:
@@ -224,7 +225,7 @@ class Worktime(object):
224 toDay = parse_single(toDay) 225 toDay = parse_single(toDay)
225 else: 226 else:
226 fromDay = toDay = parse_single(datestr) 227 fromDay = toDay = parse_single(datestr)
227 time = time_per_day 228 time = self.time_per_day
228 if len(splitLine) == 2: 229 if len(splitLine) == 2:
229 [hours, datestr] = splitLine 230 [hours, datestr] = splitLine
230 time = timedelta(hours = float(hours)) 231 time = timedelta(hours = float(hours))
@@ -236,7 +237,7 @@ class Worktime(object):
236 if end_date.date() < day or day < start_date.date(): 237 if end_date.date() < day or day < start_date.date():
237 continue 238 continue
238 239
239 if excused_kind == 'leave' and not (day in holidays and holidays[day] >= time_per_day) and day.isoweekday() in workdays: 240 if excused_kind == 'leave' and not (day in holidays and holidays[day] >= self.time_per_day) and day.isoweekday() in workdays:
240 self.leave_days.add(day) 241 self.leave_days.add(day)
241 holidays[day] = time 242 holidays[day] = time
242 except IOError as e: 243 except IOError as e:
@@ -244,7 +245,7 @@ class Worktime(object):
244 raise e 245 raise e
245 246
246 pull_forward = dict() 247 pull_forward = dict()
247 248
248 start_day = start_date.date() 249 start_day = start_date.date()
249 end_day = end_date.date() 250 end_day = end_date.date()
250 251
@@ -271,7 +272,7 @@ class Worktime(object):
271 if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break 272 if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break
272 else: 273 else:
273 if d >= end_date.date(): 274 if d >= end_date.date():
274 pull_forward[d] = min(timedelta(hours = float(hours)), time_per_day - (holidays[d] if d in holidays else timedelta())) 275 pull_forward[d] = min(timedelta(hours = float(hours)), self.time_per_day - (holidays[d] if d in holidays else timedelta()))
275 except IOError as e: 276 except IOError as e:
276 if e.errno != 2: 277 if e.errno != 2:
277 raise e 278 raise e
@@ -280,10 +281,10 @@ class Worktime(object):
280 281
281 if pull_forward: 282 if pull_forward:
282 end_day = max(end_day, max(list(pull_forward))) 283 end_day = max(end_day, max(list(pull_forward)))
283 284
284 for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]: 285 for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]:
285 if day.isoweekday() in workdays: 286 if day.isoweekday() in workdays:
286 time_to_work = time_per_day 287 time_to_work = self.time_per_day
287 if day in holidays.keys(): 288 if day in holidays.keys():
288 time_to_work -= holidays[day] 289 time_to_work -= holidays[day]
289 if time_to_work > timedelta(): 290 if time_to_work > timedelta():
@@ -302,7 +303,7 @@ class Worktime(object):
302 day = datetime.strptime(datestr, date_format).replace(tzinfo=tzlocal()).date() 303 day = datetime.strptime(datestr, date_format).replace(tzinfo=tzlocal()).date()
303 extra_days_to_work[day] = timedelta(hours = float(hours)) 304 extra_days_to_work[day] = timedelta(hours = float(hours))
304 else: 305 else:
305 extra_days_to_work[datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal()).date()] = time_per_day 306 extra_days_to_work[datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal()).date()] = self.time_per_day
306 except IOError as e: 307 except IOError as e:
307 if e.errno != 2: 308 if e.errno != 2:
308 raise e 309 raise e
@@ -329,15 +330,15 @@ class Worktime(object):
329 330
330 extra_day_time_left = timedelta() 331 extra_day_time_left = timedelta()
331 for extra_day in extra_days_forward: 332 for extra_day in extra_days_forward:
332 day_time = max(timedelta(), time_per_day - extra_days_to_work[extra_day]) 333 day_time = max(timedelta(), self.time_per_day - extra_days_to_work[extra_day])
333 extra_day_time_left += day_time 334 extra_day_time_left += day_time
334 extra_day_time = min(extra_day_time_left, pull_forward[day]) 335 extra_day_time = min(extra_day_time_left, pull_forward[day])
335 time_forward = pull_forward[day] - extra_day_time 336 time_forward = pull_forward[day] - extra_day_time
336 if extra_day_time_left > timedelta(): 337 if extra_day_time_left > timedelta():
337 for extra_day in extra_days_forward: 338 for extra_day in extra_days_forward:
338 day_time = max(timedelta(), time_per_day - extra_days_to_work[extra_day]) 339 day_time = max(timedelta(), self.time_per_day - extra_days_to_work[extra_day])
339 extra_days_to_work[extra_day] += extra_day_time * (day_time / extra_day_time_left) 340 extra_days_to_work[extra_day] += extra_day_time * (day_time / extra_day_time_left)
340 341
341 hours_per_day_forward = time_forward / len(days_forward) if len(days_forward) > 0 else timedelta() 342 hours_per_day_forward = time_forward / len(days_forward) if len(days_forward) > 0 else timedelta()
342 days_forward.discard(end_date.date()) 343 days_forward.discard(end_date.date())
343 344
@@ -345,7 +346,7 @@ class Worktime(object):
345 346
346 if end_date.date() in extra_days_to_work: 347 if end_date.date() in extra_days_to_work:
347 self.time_pulled_forward += extra_days_to_work[end_date.date()] 348 self.time_pulled_forward += extra_days_to_work[end_date.date()]
348 349
349 self.time_to_work += self.time_pulled_forward 350 self.time_to_work += self.time_pulled_forward
350 351
351 self.time_worked += api.get_billable_hours(start_date, self.now, rounding = config.getboolean('WORKTIME', 'rounding', fallback=True)) 352 self.time_worked += api.get_billable_hours(start_date, self.now, rounding = config.getboolean('WORKTIME', 'rounding', fallback=True))
@@ -377,10 +378,10 @@ def worktime(**args):
377 378
378 if total_minutes_difference >= 0: 379 if total_minutes_difference >= 0:
379 difference_string = difference_string(total_minutes_difference * timedelta(minutes = 1)) 380 difference_string = difference_string(total_minutes_difference * timedelta(minutes = 1))
380 return "{difference_string}/{clockout_time}".format(difference_string = difference_string, clockout_time = clockout_time.strftime("%H:%M")) 381 return f"{difference_string}/{clockout_time:%H:%M}"
381 else: 382 else:
382 difference_string = difference_string(abs(total_minutes_difference) * timedelta(minutes = 1)) 383 difference_string = difference_string(abs(total_minutes_difference) * timedelta(minutes = 1))
383 return "{clockout_time}/{difference_string}".format(difference_string = difference_string, clockout_time = clockout_time.strftime("%H:%M")) 384 return f"{clockout_time:%H:%M}/{difference_string}"
384 else: 385 else:
385 if worktime.running_entry: 386 if worktime.running_entry:
386 difference_string = difference_string(abs(total_minutes_difference) * timedelta(minutes = 1)) 387 difference_string = difference_string(abs(total_minutes_difference) * timedelta(minutes = 1))
@@ -427,7 +428,20 @@ def time_worked(now, **args):
427 if hours_difference == 0 or minutes_difference != 0: 428 if hours_difference == 0 or minutes_difference != 0:
428 difference_string += f"{minutes_difference}m" 429 difference_string += f"{minutes_difference}m"
429 430
430 print(difference_string) 431 clockout_time = None
432 clockout_difference = None
433 if then.is_workday or now.is_workday:
434 target_time = max(then.time_per_day, now.time_per_day) if then.time_per_day and now.time_per_day else (then.time_per_day if then.time_per_day else now.time_per_day);
435 difference = target_time - worked
436 clockout_difference = 5 * ceil(difference / timedelta(minutes = 5))
437 clockout_time = now.now + difference
438 clockout_time += (5 - clockout_time.minute % 5) * timedelta(minutes = 1)
439 clockout_time = clockout_time.replace(second = 0, microsecond = 0)
440
441 if now.running_entry and clockout_time and clockout_difference >= 0:
442 print(f"{difference_string}/{clockout_time:%H:%M}")
443 else:
444 print(difference_string)
431 else: 445 else:
432 print(worked) 446 print(worked)
433 447
@@ -445,7 +459,7 @@ def holidays(year, **args):
445 date_format = config.get('WORKTIME', 'DateFormat', fallback='%Y-%m-%d') 459 date_format = config.get('WORKTIME', 'DateFormat', fallback='%Y-%m-%d')
446 460
447 table_data = [] 461 table_data = []
448 462
449 holidays = Worktime.holidays(year) 463 holidays = Worktime.holidays(year)
450 for k, v in holidays.items(): 464 for k, v in holidays.items():
451 kstr = k.strftime(date_format) 465 kstr = k.strftime(date_format)
@@ -473,7 +487,7 @@ def leave(year, table, **args):
473 break 487 break
474 else: 488 else:
475 print(f'Unaccounted leave: {day}', file=stderr) 489 print(f'Unaccounted leave: {day}', file=stderr)
476 490
477 if table: 491 if table:
478 table_data = [] 492 table_data = []
479 for year, days in leave_budget.items(): 493 for year, days in leave_budget.items():