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(): |
