diff options
| -rw-r--r-- | overlays/worktime/default.nix | 2 | ||||
| -rwxr-xr-x | overlays/worktime/worktime.py | 68 |
2 files changed, 51 insertions, 19 deletions
diff --git a/overlays/worktime/default.nix b/overlays/worktime/default.nix index 26e1dfed..ab6fb40a 100644 --- a/overlays/worktime/default.nix +++ b/overlays/worktime/default.nix | |||
| @@ -5,7 +5,7 @@ final: prev: { | |||
| 5 | 5 | ||
| 6 | phases = [ "buildPhase" "installPhase" ]; | 6 | phases = [ "buildPhase" "installPhase" ]; |
| 7 | 7 | ||
| 8 | python = prev.python37.withPackages (ps: with ps; [pyxdg dateutil uritools requests configparser]); | 8 | python = prev.python39.withPackages (ps: with ps; [pyxdg dateutil uritools requests configparser tabulate]); |
| 9 | 9 | ||
| 10 | buildPhase = '' | 10 | buildPhase = '' |
| 11 | substituteAll $src worktime | 11 | substituteAll $src worktime |
diff --git a/overlays/worktime/worktime.py b/overlays/worktime/worktime.py index 9e514e65..b9af430a 100755 --- a/overlays/worktime/worktime.py +++ b/overlays/worktime/worktime.py | |||
| @@ -22,6 +22,10 @@ import argparse | |||
| 22 | 22 | ||
| 23 | from copy import deepcopy | 23 | from copy import deepcopy |
| 24 | 24 | ||
| 25 | import sys | ||
| 26 | |||
| 27 | from tabulate import tabulate | ||
| 28 | |||
| 25 | class TogglAPISection(Enum): | 29 | class TogglAPISection(Enum): |
| 26 | TOGGL = '/api/v8' | 30 | TOGGL = '/api/v8' |
| 27 | REPORTS = '/reports/api/v2' | 31 | REPORTS = '/reports/api/v2' |
| @@ -100,6 +104,36 @@ class Worktime(object): | |||
| 100 | time_to_work = None | 104 | time_to_work = None |
| 101 | force_day_to_work = True | 105 | force_day_to_work = True |
| 102 | 106 | ||
| 107 | @staticmethod | ||
| 108 | def holidays(year): | ||
| 109 | holidays = dict() | ||
| 110 | |||
| 111 | y_easter = datetime.combine(easter(year), time(), tzinfo=tzlocal()) | ||
| 112 | |||
| 113 | # Legal holidays in munich, bavaria | ||
| 114 | holidays[datetime(year, 1, 1, tzinfo=tzlocal()).date()] = 1 | ||
| 115 | holidays[datetime(year, 1, 6, tzinfo=tzlocal()).date()] = 1 | ||
| 116 | holidays[(y_easter+timedelta(days=-2)).date()] = 1 | ||
| 117 | holidays[(y_easter+timedelta(days=+1)).date()] = 1 | ||
| 118 | holidays[datetime(year, 5, 1, tzinfo=tzlocal()).date()] = 1 | ||
| 119 | holidays[(y_easter+timedelta(days=+39)).date()] = 1 | ||
| 120 | holidays[(y_easter+timedelta(days=+50)).date()] = 1 | ||
| 121 | holidays[(y_easter+timedelta(days=+60)).date()] = 1 | ||
| 122 | holidays[datetime(year, 8, 15, tzinfo=tzlocal()).date()] = 1 | ||
| 123 | holidays[datetime(year, 10, 3, tzinfo=tzlocal()).date()] = 1 | ||
| 124 | holidays[datetime(year, 11, 1, tzinfo=tzlocal()).date()] = 1 | ||
| 125 | holidays[datetime(year, 12, 25, tzinfo=tzlocal()).date()] = 1 | ||
| 126 | holidays[datetime(year, 12, 26, tzinfo=tzlocal()).date()] = 1 | ||
| 127 | |||
| 128 | return holidays | ||
| 129 | |||
| 130 | @staticmethod | ||
| 131 | def config(): | ||
| 132 | config = configparser.ConfigParser() | ||
| 133 | config_dir = BaseDirectory.load_first_config('worktime') | ||
| 134 | config.read(f"{config_dir}/worktime.ini") | ||
| 135 | return config | ||
| 136 | |||
| 103 | def __init__(self, start_datetime=None, end_datetime=None, now=None, include_running=True, force_day_to_work=True, **kwargs): | 137 | def __init__(self, start_datetime=None, end_datetime=None, now=None, include_running=True, force_day_to_work=True, **kwargs): |
| 104 | self.include_running = include_running | 138 | self.include_running = include_running |
| 105 | self.force_day_to_work = force_day_to_work | 139 | self.force_day_to_work = force_day_to_work |
| @@ -107,9 +141,8 @@ class Worktime(object): | |||
| 107 | if now: | 141 | if now: |
| 108 | self.now = now | 142 | self.now = now |
| 109 | 143 | ||
| 110 | config = configparser.ConfigParser() | 144 | config = Worktime.config() |
| 111 | config_dir = BaseDirectory.load_first_config('worktime') | 145 | config_dir = BaseDirectory.load_first_config('worktime') |
| 112 | config.read(f"{config_dir}/worktime.ini") | ||
| 113 | api = TogglAPI(api_token=config['TOGGL']['ApiToken'], workspace_id=config['TOGGL']['Workspace']) | 146 | api = TogglAPI(api_token=config['TOGGL']['ApiToken'], workspace_id=config['TOGGL']['Workspace']) |
| 114 | date_format = config.get('WORKTIME', 'DateFormat', fallback='%Y-%m-%d') | 147 | date_format = config.get('WORKTIME', 'DateFormat', fallback='%Y-%m-%d') |
| 115 | 148 | ||
| @@ -136,22 +169,7 @@ class Worktime(object): | |||
| 136 | holidays = dict() | 169 | holidays = dict() |
| 137 | 170 | ||
| 138 | for year in range(start_date.year, end_date.year + 1): | 171 | for year in range(start_date.year, end_date.year + 1): |
| 139 | y_easter = datetime.combine(easter(year), time(), tzinfo=tzlocal()) | 172 | holidays |= {k: v * time_per_day for k, v in Worktime.holidays(year).items()} |
| 140 | |||
| 141 | # Legal holidays in munich, bavaria | ||
| 142 | holidays[datetime(year, 1, 1, tzinfo=tzlocal()).date()] = time_per_day | ||
| 143 | holidays[datetime(year, 1, 6, tzinfo=tzlocal()).date()] = time_per_day | ||
| 144 | holidays[(y_easter+timedelta(days=-2)).date()] = time_per_day | ||
| 145 | holidays[(y_easter+timedelta(days=+1)).date()] = time_per_day | ||
| 146 | holidays[datetime(year, 5, 1, tzinfo=tzlocal()).date()] = time_per_day | ||
| 147 | holidays[(y_easter+timedelta(days=+39)).date()] = time_per_day | ||
| 148 | holidays[(y_easter+timedelta(days=+50)).date()] = time_per_day | ||
| 149 | holidays[(y_easter+timedelta(days=+60)).date()] = time_per_day | ||
| 150 | holidays[datetime(year, 8, 15, tzinfo=tzlocal()).date()] = time_per_day | ||
| 151 | holidays[datetime(year, 10, 3, tzinfo=tzlocal()).date()] = time_per_day | ||
| 152 | holidays[datetime(year, 11, 1, tzinfo=tzlocal()).date()] = time_per_day | ||
| 153 | holidays[datetime(year, 12, 25, tzinfo=tzlocal()).date()] = time_per_day | ||
| 154 | holidays[datetime(year, 12, 26, tzinfo=tzlocal()).date()] = time_per_day | ||
| 155 | 173 | ||
| 156 | try: | 174 | try: |
| 157 | with open(f"{config_dir}/excused", 'r') as excused: | 175 | with open(f"{config_dir}/excused", 'r') as excused: |
| @@ -365,7 +383,19 @@ def diff(now, **args): | |||
| 365 | now = Worktime(**dict(args, now = now, include_running = False)) | 383 | now = Worktime(**dict(args, now = now, include_running = False)) |
| 366 | 384 | ||
| 367 | print(now.time_to_work - then.time_to_work) | 385 | print(now.time_to_work - then.time_to_work) |
| 386 | |||
| 387 | def holidays(now, **args): | ||
| 388 | config = Worktime.config() | ||
| 389 | date_format = config.get('WORKTIME', 'DateFormat', fallback='%Y-%m-%d') | ||
| 390 | |||
| 391 | table_data = [] | ||
| 368 | 392 | ||
| 393 | holidays = Worktime.holidays(now.year) | ||
| 394 | for k, v in holidays.items(): | ||
| 395 | kstr = k.strftime(date_format) | ||
| 396 | |||
| 397 | table_data += [[kstr, v]] | ||
| 398 | print(tabulate(table_data, tablefmt="plain")) | ||
| 369 | 399 | ||
| 370 | def main(): | 400 | def main(): |
| 371 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using toggl API') | 401 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using toggl API') |
| @@ -379,6 +409,8 @@ def main(): | |||
| 379 | time_worked_parser.set_defaults(cmd = time_worked) | 409 | time_worked_parser.set_defaults(cmd = time_worked) |
| 380 | diff_parser = subparsers.add_parser('diff') | 410 | diff_parser = subparsers.add_parser('diff') |
| 381 | diff_parser.set_defaults(cmd = diff) | 411 | diff_parser.set_defaults(cmd = diff) |
| 412 | holidays_parser = subparsers.add_parser('holidays') | ||
| 413 | holidays_parser.set_defaults(cmd = holidays) | ||
| 382 | args = parser.parse_args() | 414 | args = parser.parse_args() |
| 383 | 415 | ||
| 384 | args.cmd(**vars(args)) | 416 | args.cmd(**vars(args)) |
