diff options
Diffstat (limited to 'overlays/worktime')
-rwxr-xr-x | overlays/worktime/worktime.py | 58 |
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(): |