diff options
| author | Gregor Kleen <gkleen@yggdrasil.li> | 2025-05-25 11:06:00 +0200 |
|---|---|---|
| committer | Gregor Kleen <gkleen@yggdrasil.li> | 2025-05-25 11:06:00 +0200 |
| commit | 15b3cfbf4a4ed2c85cfd8eea3e67da87c0c37db6 (patch) | |
| tree | d84e1923a39829a302e35fd9a9f6201df63df552 /overlays | |
| parent | e0555619bd00dae082e625235cbe89d38987f4fe (diff) | |
| download | nixos-15b3cfbf4a4ed2c85cfd8eea3e67da87c0c37db6.tar nixos-15b3cfbf4a4ed2c85cfd8eea3e67da87c0c37db6.tar.gz nixos-15b3cfbf4a4ed2c85cfd8eea3e67da87c0c37db6.tar.bz2 nixos-15b3cfbf4a4ed2c85cfd8eea3e67da87c0c37db6.tar.xz nixos-15b3cfbf4a4ed2c85cfd8eea3e67da87c0c37db6.zip | |
migrate worktime to kimai
Diffstat (limited to 'overlays')
| -rw-r--r-- | overlays/worktime/.envrc | 4 | ||||
| -rw-r--r-- | overlays/worktime/.gitignore | 2 | ||||
| -rw-r--r-- | overlays/worktime/default.nix | 26 | ||||
| -rw-r--r-- | overlays/worktime/poetry.lock | 284 | ||||
| -rw-r--r-- | overlays/worktime/pyproject.toml | 36 | ||||
| -rw-r--r-- | overlays/worktime/uv.lock | 174 | ||||
| -rwxr-xr-x | overlays/worktime/worktime/__main__.py | 225 |
7 files changed, 302 insertions, 449 deletions
diff --git a/overlays/worktime/.envrc b/overlays/worktime/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/worktime/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/overlays/worktime/.gitignore b/overlays/worktime/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/worktime/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/overlays/worktime/default.nix b/overlays/worktime/default.nix index 1d8433af..579cf7ad 100644 --- a/overlays/worktime/default.nix +++ b/overlays/worktime/default.nix | |||
| @@ -1,13 +1,19 @@ | |||
| 1 | { prev, ... }: | 1 | { prev, final, flake, flakeInputs, ... }: |
| 2 | 2 | ||
| 3 | with prev.poetry2nix; | 3 | let |
| 4 | 4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | |
| 5 | { | 5 | pythonSet = flake.lib.pythonSet { |
| 6 | worktime = mkPoetryApplication { | 6 | pkgs = final; |
| 7 | python = prev.python312; | 7 | python = final.python312; |
| 8 | 8 | overlay = workspace.mkPyprojectOverlay { | |
| 9 | projectDir = cleanPythonSources { src = ./.; }; | 9 | sourcePreference = "wheel"; |
| 10 | 10 | }; | |
| 11 | meta.mainProgram = "worktime"; | ||
| 12 | }; | 11 | }; |
| 12 | virtualEnv = pythonSet.mkVirtualEnv "worktime" workspace.deps.default; | ||
| 13 | in { | ||
| 14 | worktime = virtualEnv.overrideAttrs (oldAttrs: { | ||
| 15 | meta = (oldAttrs.meta or {}) // { | ||
| 16 | mainProgram = "worktime"; | ||
| 17 | }; | ||
| 18 | }); | ||
| 13 | } | 19 | } |
diff --git a/overlays/worktime/poetry.lock b/overlays/worktime/poetry.lock deleted file mode 100644 index 7c1ca91d..00000000 --- a/overlays/worktime/poetry.lock +++ /dev/null | |||
| @@ -1,284 +0,0 @@ | |||
| 1 | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. | ||
| 2 | |||
| 3 | [[package]] | ||
| 4 | name = "backoff" | ||
| 5 | version = "2.2.1" | ||
| 6 | description = "Function decoration for backoff and retry" | ||
| 7 | optional = false | ||
| 8 | python-versions = ">=3.7,<4.0" | ||
| 9 | groups = ["main"] | ||
| 10 | files = [ | ||
| 11 | {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, | ||
| 12 | {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, | ||
| 13 | ] | ||
| 14 | |||
| 15 | [[package]] | ||
| 16 | name = "certifi" | ||
| 17 | version = "2025.1.31" | ||
| 18 | description = "Python package for providing Mozilla's CA Bundle." | ||
| 19 | optional = false | ||
| 20 | python-versions = ">=3.6" | ||
| 21 | groups = ["main"] | ||
| 22 | files = [ | ||
| 23 | {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, | ||
| 24 | {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, | ||
| 25 | ] | ||
| 26 | |||
| 27 | [[package]] | ||
| 28 | name = "charset-normalizer" | ||
| 29 | version = "3.4.1" | ||
| 30 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." | ||
| 31 | optional = false | ||
| 32 | python-versions = ">=3.7" | ||
| 33 | groups = ["main"] | ||
| 34 | files = [ | ||
| 35 | {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, | ||
| 36 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, | ||
| 37 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, | ||
| 38 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, | ||
| 39 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, | ||
| 40 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, | ||
| 41 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, | ||
| 42 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, | ||
| 43 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, | ||
| 44 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, | ||
| 45 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, | ||
| 46 | {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, | ||
| 47 | {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, | ||
| 48 | {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, | ||
| 49 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, | ||
| 50 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, | ||
| 51 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, | ||
| 52 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, | ||
| 53 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, | ||
| 54 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, | ||
| 55 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, | ||
| 56 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, | ||
| 57 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, | ||
| 58 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, | ||
| 59 | {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, | ||
| 60 | {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, | ||
| 61 | {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, | ||
| 62 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, | ||
| 63 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, | ||
| 64 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, | ||
| 65 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, | ||
| 66 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, | ||
| 67 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, | ||
| 68 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, | ||
| 69 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, | ||
| 70 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, | ||
| 71 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, | ||
| 72 | {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, | ||
| 73 | {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, | ||
| 74 | {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, | ||
| 75 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, | ||
| 76 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, | ||
| 77 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, | ||
| 78 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, | ||
| 79 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, | ||
| 80 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, | ||
| 81 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, | ||
| 82 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, | ||
| 83 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, | ||
| 84 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, | ||
| 85 | {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, | ||
| 86 | {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, | ||
| 87 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, | ||
| 88 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, | ||
| 89 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, | ||
| 90 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, | ||
| 91 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, | ||
| 92 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, | ||
| 93 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, | ||
| 94 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, | ||
| 95 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, | ||
| 96 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, | ||
| 97 | {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, | ||
| 98 | {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, | ||
| 99 | {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, | ||
| 100 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, | ||
| 101 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, | ||
| 102 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, | ||
| 103 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, | ||
| 104 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, | ||
| 105 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, | ||
| 106 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, | ||
| 107 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, | ||
| 108 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, | ||
| 109 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, | ||
| 110 | {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, | ||
| 111 | {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, | ||
| 112 | {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, | ||
| 113 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, | ||
| 114 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, | ||
| 115 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, | ||
| 116 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, | ||
| 117 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, | ||
| 118 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, | ||
| 119 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, | ||
| 120 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, | ||
| 121 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, | ||
| 122 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, | ||
| 123 | {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, | ||
| 124 | {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, | ||
| 125 | {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, | ||
| 126 | {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, | ||
| 127 | ] | ||
| 128 | |||
| 129 | [[package]] | ||
| 130 | name = "idna" | ||
| 131 | version = "3.10" | ||
| 132 | description = "Internationalized Domain Names in Applications (IDNA)" | ||
| 133 | optional = false | ||
| 134 | python-versions = ">=3.6" | ||
| 135 | groups = ["main"] | ||
| 136 | files = [ | ||
| 137 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, | ||
| 138 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, | ||
| 139 | ] | ||
| 140 | |||
| 141 | [package.extras] | ||
| 142 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] | ||
| 143 | |||
| 144 | [[package]] | ||
| 145 | name = "jsonpickle" | ||
| 146 | version = "4.0.5" | ||
| 147 | description = "jsonpickle encodes/decodes any Python object to/from JSON" | ||
| 148 | optional = false | ||
| 149 | python-versions = ">=3.8" | ||
| 150 | groups = ["main"] | ||
| 151 | files = [ | ||
| 152 | {file = "jsonpickle-4.0.5-py3-none-any.whl", hash = "sha256:b4ac7d0a75ddcdfd93445737f1d36ff28768690d43e54bf5d0ddb1d915e580df"}, | ||
| 153 | {file = "jsonpickle-4.0.5.tar.gz", hash = "sha256:f299818b39367c361b3f26bdba827d4249ab5d383cd93144d0f94b5417aacb35"}, | ||
| 154 | ] | ||
| 155 | |||
| 156 | [package.extras] | ||
| 157 | cov = ["pytest-cov"] | ||
| 158 | dev = ["black", "pyupgrade"] | ||
| 159 | docs = ["furo", "rst.linker (>=1.9)", "sphinx (>=3.5)"] | ||
| 160 | packaging = ["build", "setuptools (>=61.2)", "setuptools-scm[toml] (>=6.0)", "twine"] | ||
| 161 | testing = ["PyYAML", "atheris (>=2.3.0,<2.4.0) ; python_version < \"3.12\"", "bson", "ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=6.0,!=8.1.*)", "pytest-benchmark", "pytest-benchmark[histogram]", "pytest-checkdocs (>=1.2.3)", "pytest-enabler (>=1.0.1)", "pytest-ruff (>=0.2.1)", "scikit-learn", "scipy (>=1.9.3) ; python_version > \"3.10\"", "scipy ; python_version <= \"3.10\"", "simplejson", "sqlalchemy", "ujson"] | ||
| 162 | |||
| 163 | [[package]] | ||
| 164 | name = "python-dateutil" | ||
| 165 | version = "2.9.0.post0" | ||
| 166 | description = "Extensions to the standard Python datetime module" | ||
| 167 | optional = false | ||
| 168 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
| 169 | groups = ["main"] | ||
| 170 | files = [ | ||
| 171 | {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, | ||
| 172 | {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, | ||
| 173 | ] | ||
| 174 | |||
| 175 | [package.dependencies] | ||
| 176 | six = ">=1.5" | ||
| 177 | |||
| 178 | [[package]] | ||
| 179 | name = "pyxdg" | ||
| 180 | version = "0.28" | ||
| 181 | description = "PyXDG contains implementations of freedesktop.org standards in python." | ||
| 182 | optional = false | ||
| 183 | python-versions = "*" | ||
| 184 | groups = ["main"] | ||
| 185 | files = [ | ||
| 186 | {file = "pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"}, | ||
| 187 | {file = "pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4"}, | ||
| 188 | ] | ||
| 189 | |||
| 190 | [[package]] | ||
| 191 | name = "requests" | ||
| 192 | version = "2.32.3" | ||
| 193 | description = "Python HTTP for Humans." | ||
| 194 | optional = false | ||
| 195 | python-versions = ">=3.8" | ||
| 196 | groups = ["main"] | ||
| 197 | files = [ | ||
| 198 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, | ||
| 199 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, | ||
| 200 | ] | ||
| 201 | |||
| 202 | [package.dependencies] | ||
| 203 | certifi = ">=2017.4.17" | ||
| 204 | charset-normalizer = ">=2,<4" | ||
| 205 | idna = ">=2.5,<4" | ||
| 206 | urllib3 = ">=1.21.1,<3" | ||
| 207 | |||
| 208 | [package.extras] | ||
| 209 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] | ||
| 210 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] | ||
| 211 | |||
| 212 | [[package]] | ||
| 213 | name = "six" | ||
| 214 | version = "1.17.0" | ||
| 215 | description = "Python 2 and 3 compatibility utilities" | ||
| 216 | optional = false | ||
| 217 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
| 218 | groups = ["main"] | ||
| 219 | files = [ | ||
| 220 | {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, | ||
| 221 | {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, | ||
| 222 | ] | ||
| 223 | |||
| 224 | [[package]] | ||
| 225 | name = "tabulate" | ||
| 226 | version = "0.9.0" | ||
| 227 | description = "Pretty-print tabular data" | ||
| 228 | optional = false | ||
| 229 | python-versions = ">=3.7" | ||
| 230 | groups = ["main"] | ||
| 231 | files = [ | ||
| 232 | {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, | ||
| 233 | {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, | ||
| 234 | ] | ||
| 235 | |||
| 236 | [package.extras] | ||
| 237 | widechars = ["wcwidth"] | ||
| 238 | |||
| 239 | [[package]] | ||
| 240 | name = "toml" | ||
| 241 | version = "0.10.2" | ||
| 242 | description = "Python Library for Tom's Obvious, Minimal Language" | ||
| 243 | optional = false | ||
| 244 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" | ||
| 245 | groups = ["main"] | ||
| 246 | files = [ | ||
| 247 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, | ||
| 248 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, | ||
| 249 | ] | ||
| 250 | |||
| 251 | [[package]] | ||
| 252 | name = "uritools" | ||
| 253 | version = "4.0.3" | ||
| 254 | description = "URI parsing, classification and composition" | ||
| 255 | optional = false | ||
| 256 | python-versions = ">=3.7" | ||
| 257 | groups = ["main"] | ||
| 258 | files = [ | ||
| 259 | {file = "uritools-4.0.3-py3-none-any.whl", hash = "sha256:bae297d090e69a0451130ffba6f2f1c9477244aa0a5543d66aed2d9f77d0dd9c"}, | ||
| 260 | {file = "uritools-4.0.3.tar.gz", hash = "sha256:ee06a182a9c849464ce9d5fa917539aacc8edd2a4924d1b7aabeeecabcae3bc2"}, | ||
| 261 | ] | ||
| 262 | |||
| 263 | [[package]] | ||
| 264 | name = "urllib3" | ||
| 265 | version = "2.3.0" | ||
| 266 | description = "HTTP library with thread-safe connection pooling, file post, and more." | ||
| 267 | optional = false | ||
| 268 | python-versions = ">=3.9" | ||
| 269 | groups = ["main"] | ||
| 270 | files = [ | ||
| 271 | {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, | ||
| 272 | {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, | ||
| 273 | ] | ||
| 274 | |||
| 275 | [package.extras] | ||
| 276 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] | ||
| 277 | h2 = ["h2 (>=4,<5)"] | ||
| 278 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] | ||
| 279 | zstd = ["zstandard (>=0.18.0)"] | ||
| 280 | |||
| 281 | [metadata] | ||
| 282 | lock-version = "2.1" | ||
| 283 | python-versions = "^3.12" | ||
| 284 | content-hash = "2b335da94bf3e2d2bee7d8ca6e84cdb56e97ac29d1224d8c8dca98d93bbdcea2" | ||
diff --git a/overlays/worktime/pyproject.toml b/overlays/worktime/pyproject.toml index de4b9fd4..bb6934ac 100644 --- a/overlays/worktime/pyproject.toml +++ b/overlays/worktime/pyproject.toml | |||
| @@ -1,23 +1,23 @@ | |||
| 1 | [tool.poetry] | 1 | [project] |
| 2 | name = "worktime" | 2 | name = "worktime" |
| 3 | version = "0.1.0" | 3 | version = "1.0.0" |
| 4 | description = "" | 4 | requires-python = "~=3.12" |
| 5 | authors = ["Gregor Kleen <gkleen@yggdrasil.li>"] | 5 | dependencies = [ |
| 6 | "pyxdg>=0.28,<0.29", | ||
| 7 | "python-dateutil>=2.9.0.post0,<3", | ||
| 8 | "uritools>=4.0.3,<5", | ||
| 9 | "requests>=2.32.3,<3", | ||
| 10 | "tabulate>=0.9.0,<0.10", | ||
| 11 | "toml>=0.10.2,<0.11", | ||
| 12 | "jsonpickle>=4.0.5,<5", | ||
| 13 | ] | ||
| 6 | 14 | ||
| 7 | [tool.poetry.dependencies] | 15 | [project.scripts] |
| 8 | python = "^3.12" | ||
| 9 | pyxdg = "^0.28" | ||
| 10 | python-dateutil = "^2.9.0.post0" | ||
| 11 | uritools = "^4.0.3" | ||
| 12 | requests = "^2.32.3" | ||
| 13 | tabulate = "^0.9.0" | ||
| 14 | backoff = "^2.2.1" | ||
| 15 | toml = "^0.10.2" | ||
| 16 | jsonpickle = "^4.0.5" | ||
| 17 | |||
| 18 | [tool.poetry.scripts] | ||
| 19 | worktime = "worktime.__main__:main" | 16 | worktime = "worktime.__main__:main" |
| 20 | 17 | ||
| 21 | [build-system] | 18 | [build-system] |
| 22 | requires = ["poetry-core"] | 19 | requires = ["hatchling"] |
| 23 | build-backend = "poetry.core.masonry.api" \ No newline at end of file | 20 | build-backend = "hatchling.build" |
| 21 | |||
| 22 | [dependency-groups] | ||
| 23 | dev = [] | ||
diff --git a/overlays/worktime/uv.lock b/overlays/worktime/uv.lock new file mode 100644 index 00000000..8f91b44b --- /dev/null +++ b/overlays/worktime/uv.lock | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12, <4" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "certifi" | ||
| 7 | version = "2025.1.31" | ||
| 8 | source = { registry = "https://pypi.org/simple" } | ||
| 9 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload-time = "2025-01-31T02:16:47.166Z" } | ||
| 10 | wheels = [ | ||
| 11 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" }, | ||
| 12 | ] | ||
| 13 | |||
| 14 | [[package]] | ||
| 15 | name = "charset-normalizer" | ||
| 16 | version = "3.4.1" | ||
| 17 | source = { registry = "https://pypi.org/simple" } | ||
| 18 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } | ||
| 19 | wheels = [ | ||
| 20 | { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" }, | ||
| 21 | { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" }, | ||
| 22 | { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" }, | ||
| 23 | { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" }, | ||
| 24 | { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" }, | ||
| 25 | { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" }, | ||
| 26 | { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" }, | ||
| 27 | { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" }, | ||
| 28 | { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" }, | ||
| 29 | { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" }, | ||
| 30 | { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" }, | ||
| 31 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" }, | ||
| 32 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" }, | ||
| 33 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" }, | ||
| 34 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" }, | ||
| 35 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" }, | ||
| 36 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" }, | ||
| 37 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" }, | ||
| 38 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" }, | ||
| 39 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" }, | ||
| 40 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" }, | ||
| 41 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" }, | ||
| 42 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" }, | ||
| 43 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" }, | ||
| 44 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" }, | ||
| 45 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" }, | ||
| 46 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, | ||
| 47 | ] | ||
| 48 | |||
| 49 | [[package]] | ||
| 50 | name = "idna" | ||
| 51 | version = "3.10" | ||
| 52 | source = { registry = "https://pypi.org/simple" } | ||
| 53 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } | ||
| 54 | wheels = [ | ||
| 55 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, | ||
| 56 | ] | ||
| 57 | |||
| 58 | [[package]] | ||
| 59 | name = "jsonpickle" | ||
| 60 | version = "4.0.5" | ||
| 61 | source = { registry = "https://pypi.org/simple" } | ||
| 62 | sdist = { url = "https://files.pythonhosted.org/packages/d6/33/4bda317ab294722fcdfff8f63aab74af9fda3675a4652d984a101aa7587e/jsonpickle-4.0.5.tar.gz", hash = "sha256:f299818b39367c361b3f26bdba827d4249ab5d383cd93144d0f94b5417aacb35", size = 315661, upload-time = "2025-03-29T19:22:56.92Z" } | ||
| 63 | wheels = [ | ||
| 64 | { url = "https://files.pythonhosted.org/packages/dc/1b/0e79cf115e0f54f1e8f56effb6ffd2ef8f92e9c324d692ede660067f1bfe/jsonpickle-4.0.5-py3-none-any.whl", hash = "sha256:b4ac7d0a75ddcdfd93445737f1d36ff28768690d43e54bf5d0ddb1d915e580df", size = 46382, upload-time = "2025-03-29T19:22:54.252Z" }, | ||
| 65 | ] | ||
| 66 | |||
| 67 | [[package]] | ||
| 68 | name = "python-dateutil" | ||
| 69 | version = "2.9.0.post0" | ||
| 70 | source = { registry = "https://pypi.org/simple" } | ||
| 71 | dependencies = [ | ||
| 72 | { name = "six" }, | ||
| 73 | ] | ||
| 74 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } | ||
| 75 | wheels = [ | ||
| 76 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, | ||
| 77 | ] | ||
| 78 | |||
| 79 | [[package]] | ||
| 80 | name = "pyxdg" | ||
| 81 | version = "0.28" | ||
| 82 | source = { registry = "https://pypi.org/simple" } | ||
| 83 | sdist = { url = "https://files.pythonhosted.org/packages/b0/25/7998cd2dec731acbd438fbf91bc619603fc5188de0a9a17699a781840452/pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4", size = 77776, upload-time = "2022-06-05T11:35:01Z" } | ||
| 84 | wheels = [ | ||
| 85 | { url = "https://files.pythonhosted.org/packages/e5/8d/cf41b66a8110670e3ad03dab9b759704eeed07fa96e90fdc0357b2ba70e2/pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab", size = 49520, upload-time = "2022-06-05T11:34:58.832Z" }, | ||
| 86 | ] | ||
| 87 | |||
| 88 | [[package]] | ||
| 89 | name = "requests" | ||
| 90 | version = "2.32.3" | ||
| 91 | source = { registry = "https://pypi.org/simple" } | ||
| 92 | dependencies = [ | ||
| 93 | { name = "certifi" }, | ||
| 94 | { name = "charset-normalizer" }, | ||
| 95 | { name = "idna" }, | ||
| 96 | { name = "urllib3" }, | ||
| 97 | ] | ||
| 98 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } | ||
| 99 | wheels = [ | ||
| 100 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, | ||
| 101 | ] | ||
| 102 | |||
| 103 | [[package]] | ||
| 104 | name = "six" | ||
| 105 | version = "1.17.0" | ||
| 106 | source = { registry = "https://pypi.org/simple" } | ||
| 107 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } | ||
| 108 | wheels = [ | ||
| 109 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, | ||
| 110 | ] | ||
| 111 | |||
| 112 | [[package]] | ||
| 113 | name = "tabulate" | ||
| 114 | version = "0.9.0" | ||
| 115 | source = { registry = "https://pypi.org/simple" } | ||
| 116 | sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } | ||
| 117 | wheels = [ | ||
| 118 | { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, | ||
| 119 | ] | ||
| 120 | |||
| 121 | [[package]] | ||
| 122 | name = "toml" | ||
| 123 | version = "0.10.2" | ||
| 124 | source = { registry = "https://pypi.org/simple" } | ||
| 125 | sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } | ||
| 126 | wheels = [ | ||
| 127 | { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, | ||
| 128 | ] | ||
| 129 | |||
| 130 | [[package]] | ||
| 131 | name = "uritools" | ||
| 132 | version = "4.0.3" | ||
| 133 | source = { registry = "https://pypi.org/simple" } | ||
| 134 | sdist = { url = "https://files.pythonhosted.org/packages/d3/43/4182fb2a03145e6d38698e38b49114ce59bc8c79063452eb585a58f8ce78/uritools-4.0.3.tar.gz", hash = "sha256:ee06a182a9c849464ce9d5fa917539aacc8edd2a4924d1b7aabeeecabcae3bc2", size = 24184, upload-time = "2024-05-28T18:07:45.194Z" } | ||
| 135 | wheels = [ | ||
| 136 | { url = "https://files.pythonhosted.org/packages/e6/17/5a4510d9ca9cc8be217ce359eb54e693dca81cf4d442308b282d5131b17d/uritools-4.0.3-py3-none-any.whl", hash = "sha256:bae297d090e69a0451130ffba6f2f1c9477244aa0a5543d66aed2d9f77d0dd9c", size = 10304, upload-time = "2024-05-28T18:07:42.731Z" }, | ||
| 137 | ] | ||
| 138 | |||
| 139 | [[package]] | ||
| 140 | name = "urllib3" | ||
| 141 | version = "2.3.0" | ||
| 142 | source = { registry = "https://pypi.org/simple" } | ||
| 143 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268, upload-time = "2024-12-22T07:47:30.032Z" } | ||
| 144 | wheels = [ | ||
| 145 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" }, | ||
| 146 | ] | ||
| 147 | |||
| 148 | [[package]] | ||
| 149 | name = "worktime" | ||
| 150 | version = "1.0.0" | ||
| 151 | source = { editable = "." } | ||
| 152 | dependencies = [ | ||
| 153 | { name = "jsonpickle" }, | ||
| 154 | { name = "python-dateutil" }, | ||
| 155 | { name = "pyxdg" }, | ||
| 156 | { name = "requests" }, | ||
| 157 | { name = "tabulate" }, | ||
| 158 | { name = "toml" }, | ||
| 159 | { name = "uritools" }, | ||
| 160 | ] | ||
| 161 | |||
| 162 | [package.metadata] | ||
| 163 | requires-dist = [ | ||
| 164 | { name = "jsonpickle", specifier = ">=4.0.5,<5" }, | ||
| 165 | { name = "python-dateutil", specifier = ">=2.9.0.post0,<3" }, | ||
| 166 | { name = "pyxdg", specifier = ">=0.28,<0.29" }, | ||
| 167 | { name = "requests", specifier = ">=2.32.3,<3" }, | ||
| 168 | { name = "tabulate", specifier = ">=0.9.0,<0.10" }, | ||
| 169 | { name = "toml", specifier = ">=0.10.2,<0.11" }, | ||
| 170 | { name = "uritools", specifier = ">=4.0.3,<5" }, | ||
| 171 | ] | ||
| 172 | |||
| 173 | [package.metadata.requires-dev] | ||
| 174 | dev = [] | ||
diff --git a/overlays/worktime/worktime/__main__.py b/overlays/worktime/worktime/__main__.py index 4eee5dc2..dbe7502e 100755 --- a/overlays/worktime/worktime/__main__.py +++ b/overlays/worktime/worktime/__main__.py | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | import requests | 1 | import requests |
| 2 | from requests.exceptions import HTTPError | 2 | from requests.exceptions import HTTPError |
| 3 | from requests.auth import HTTPBasicAuth | 3 | from requests.auth import HTTPBasicAuth |
| 4 | from requests.adapters import HTTPAdapter, Retry | ||
| 4 | from datetime import * | 5 | from datetime import * |
| 5 | from xdg import BaseDirectory | 6 | from xdg import BaseDirectory |
| 6 | import toml | 7 | import toml |
| 7 | from uritools import (uricompose) | 8 | from uritools import uricompose |
| 9 | from urllib.parse import urljoin | ||
| 8 | 10 | ||
| 9 | from inspect import signature | 11 | from inspect import signature |
| 10 | 12 | ||
| @@ -30,74 +32,64 @@ from tabulate import tabulate | |||
| 30 | from itertools import groupby, count | 32 | from itertools import groupby, count |
| 31 | from functools import cache, partial | 33 | from functools import cache, partial |
| 32 | 34 | ||
| 33 | import backoff | ||
| 34 | |||
| 35 | from pathlib import Path | 35 | from pathlib import Path |
| 36 | 36 | ||
| 37 | from collections import defaultdict | 37 | from collections import defaultdict |
| 38 | from collections.abc import Iterable, Generator | ||
| 39 | from typing import Any | ||
| 38 | 40 | ||
| 39 | import jsonpickle | 41 | import jsonpickle |
| 40 | from hashlib import blake2s | 42 | from hashlib import blake2s |
| 41 | import json | 43 | import json |
| 42 | 44 | ||
| 43 | class TogglAPISection(Enum): | 45 | class BearerAuth(requests.auth.AuthBase): |
| 44 | TOGGL = '/api/v9' | 46 | def __init__(self, token): |
| 45 | REPORTS = '/reports/api/v2' | 47 | self.token = token |
| 46 | 48 | def __call__(self, r): | |
| 47 | class TogglAPIError(Exception): | 49 | r.headers["authorization"] = "Bearer " + self.token |
| 48 | def __init__(self, response, *, http_error=None): | 50 | return r |
| 49 | self.http_error = http_error | 51 | |
| 50 | self.response = response | 52 | class KimaiSession(requests.Session): |
| 51 | 53 | def __init__(self, base_url: str, api_token: str): | |
| 52 | def __str__(self): | 54 | super().__init__() |
| 53 | if not self.http_error is None: | 55 | self.base_url = base_url |
| 54 | return str(self.http_error) | 56 | self.auth = BearerAuth(api_token) |
| 55 | else: | 57 | retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) |
| 56 | return self.response.text | 58 | super().mount(base_url, HTTPAdapter(max_retries=retries)) |
| 57 | 59 | ||
| 58 | class TogglAPI(object): | 60 | def request(self, method, url, *args, **kwargs): |
| 59 | def __init__(self, api_token, workspace_id, client_ids): | 61 | joined_url = urljoin(self.base_url, url) |
| 60 | self._api_token = api_token | 62 | return super().request(method, joined_url, *args, headers = {'Accept': 'application/json'} | (kwargs['headers'] if 'headers' in kwargs else {}), **{k: v for k, v in kwargs.items() if k not in ['headers']}) |
| 61 | self._workspace_id = workspace_id | 63 | |
| 62 | self._client_ids = set(map(int, client_ids.split(','))) if client_ids else None | 64 | class KimaiAPI(object): |
| 63 | 65 | def __init__(self, base_url: str, api_token: str, clients: Iterable[str]): | |
| 64 | def _make_url(self, api=TogglAPISection.TOGGL, section=['me', 'time_entries', 'current'], params={}): | 66 | self._session = KimaiSession(base_url, api_token) |
| 65 | if api is TogglAPISection.REPORTS: | 67 | self._kimai_clients = self._session.get('/api/customers').json() |
| 66 | params.update({'user_agent': 'worktime', 'workspace_id': self._workspace_id}) | 68 | self._client_ids = self.resolve_clients(clients) |
| 67 | 69 | kimai_user = self._session.get('/api/users/me').json() | |
| 68 | api_path = api.value | 70 | self._tz = gettz(kimai_user['timezone']) |
| 69 | section_path = '/'.join(section) | 71 | |
| 70 | uri = uricompose(scheme='https', host='api.track.toggl.com', path=f"{api_path}/{section_path}", query=params) | 72 | def resolve_clients(self, clients: Iterable[str]) -> frozenset[int]: |
| 71 | 73 | return frozenset({ client['id'] for client in self._kimai_clients if client['name'] in clients }) | |
| 72 | return uri | 74 | |
| 73 | 75 | def render_datetime(self, datetime: datetime) -> str: | |
| 74 | def _query(self, url, method): | 76 | return datetime.astimezone(self._tz).strftime('%Y-%m-%dT%H:%M:%S') |
| 75 | response = self._raw_query(url, method) | 77 | |
| 76 | response.raise_for_status() | 78 | def get_timesheets(self, params: dict[str, Any] = {}) -> Generator[Any]: |
| 77 | return response | 79 | for page in count(start=1): |
| 78 | 80 | resp = self._session.get('/api/timesheets', params=params | {'size': 100, 'page': page}) | |
| 79 | @backoff.on_predicate( | 81 | if resp.status_code == 404: |
| 80 | backoff.expo, | 82 | break |
| 81 | factor=0.1, max_value=2, | 83 | yield from resp.json() |
| 82 | predicate=lambda r: r.status_code == 429, | ||
| 83 | max_time=10, | ||
| 84 | ) | ||
| 85 | def _raw_query(self, url, method): | ||
| 86 | headers = {'content-type': 'application/json', 'accept': 'application/json'} | ||
| 87 | response = None | ||
| 88 | |||
| 89 | if method == 'GET': | ||
| 90 | response = requests.get(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | ||
| 91 | elif method == 'POST': | ||
| 92 | response = requests.post(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | ||
| 93 | else: | ||
| 94 | raise ValueError(f"Undefined HTTP method “{method}”") | ||
| 95 | |||
| 96 | return response | ||
| 97 | 84 | ||
| 98 | def entry_durations(self, start_date, *, end_date, rounding=False, client_ids): | 85 | def entry_durations(self, start_date: datetime, *, end_date: datetime, clients: Iterable[str] | None = None) -> Generator[timedelta]: |
| 99 | if client_ids is not None and not client_ids: | 86 | client_ids = None |
| 87 | if clients is not None and not clients: | ||
| 100 | return | 88 | return |
| 89 | elif clients is None: | ||
| 90 | client_ids = self._client_ids | ||
| 91 | else: | ||
| 92 | client_ids = self.resolve_clients(clients) | ||
| 101 | 93 | ||
| 102 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' | 94 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' |
| 103 | step = timedelta(days = 120) | 95 | step = timedelta(days = 120) |
| @@ -116,11 +108,8 @@ class TogglAPI(object): | |||
| 116 | cache_key = blake2s(jsonpickle.encode({ | 108 | cache_key = blake2s(jsonpickle.encode({ |
| 117 | 'start': req_start, | 109 | 'start': req_start, |
| 118 | 'end': req_end, | 110 | 'end': req_end, |
| 119 | 'rounding': rounding, | 111 | 'client_ids': client_ids, |
| 120 | 'clients': client_ids, | 112 | }).encode('utf-8'), key = self._session.auth.token.encode('utf-8')).hexdigest() |
| 121 | 'workspace': self._workspace_id, | ||
| 122 | 'workspace_clients': self._client_ids | ||
| 123 | }).encode('utf-8'), key = self._api_token.encode('utf-8')).hexdigest() | ||
| 124 | cache_path = cache_dir / cache_key[:2] / cache_key[2:4] / f'{cache_key[4:]}.json' | 113 | cache_path = cache_dir / cache_key[:2] / cache_key[2:4] / f'{cache_key[4:]}.json' |
| 125 | try: | 114 | try: |
| 126 | with cache_path.open('r', encoding='utf-8') as ch: | 115 | with cache_path.open('r', encoding='utf-8') as ch: |
| @@ -130,84 +119,46 @@ class TogglAPI(object): | |||
| 130 | pass | 119 | pass |
| 131 | 120 | ||
| 132 | entries = list() | 121 | entries = list() |
| 133 | params = { 'since': (req_start - timedelta(days=1)).date().isoformat(), | 122 | params = { |
| 134 | 'until': (req_end + timedelta(days=1)).date().isoformat(), | 123 | 'begin': self.render_datetime(req_start), |
| 135 | 'rounding': 'yes' if rounding else 'no', | 124 | 'end': self.render_datetime(req_end), |
| 136 | 'billable': 'yes' | 125 | 'customers[]': list(client_ids), |
| 137 | } | 126 | 'billable': 1, |
| 138 | if client_ids is not None: | 127 | } |
| 139 | params |= { 'client_ids': ','.join(map(str, client_ids)) } | 128 | |
| 140 | for page in count(start = 1): | 129 | for entry in self.get_timesheets(params): |
| 141 | url = self._make_url(api = TogglAPISection.REPORTS, section = ['details'], params = params | { 'page': page }) | 130 | if entry['end'] is None: |
| 142 | r = self._query(url = url, method='GET') | 131 | continue |
| 143 | if not r or not r.json(): | 132 | |
| 144 | raise TogglAPIError(r) | 133 | start = isoparse(entry['begin']) |
| 145 | report = r.json() | 134 | end = isoparse(entry['end']) |
| 146 | for entry in report['data']: | ||
| 147 | start = isoparse(entry['start']) | ||
| 148 | end = isoparse(entry['end']) | ||
| 149 | |||
| 150 | if start > req_end or end < req_start: | ||
| 151 | continue | ||
| 152 | 135 | ||
| 153 | x = min(end, req_end) - max(start, req_start) | 136 | if start > req_end or end < req_start: |
| 154 | if cache_key: | 137 | continue |
| 155 | entries.append(x) | 138 | |
| 156 | yield x | 139 | x = min(end, req_end) - max(start, req_start) |
| 157 | if not report['data']: | 140 | if cache_key: |
| 158 | break | 141 | entries.append(x) |
| 142 | yield x | ||
| 159 | 143 | ||
| 160 | if cache_path: | 144 | if cache_path: |
| 161 | cache_path.parent.mkdir(parents=True, exist_ok=True) | 145 | cache_path.parent.mkdir(parents=True, exist_ok=True) |
| 162 | with cache_path.open('w', encoding='utf-8') as ch: | 146 | with cache_path.open('w', encoding='utf-8') as ch: |
| 163 | ch.write(jsonpickle.encode(entries)) | 147 | ch.write(jsonpickle.encode(entries)) |
| 164 | # res = timedelta(milliseconds=report['total_billable']) if report['total_billable'] else timedelta(milliseconds=0) | ||
| 165 | # return res | ||
| 166 | |||
| 167 | def get_billable_hours(self, start_date, end_date=datetime.now(timezone.utc), rounding=False): | ||
| 168 | billable_acc = timedelta(milliseconds = 0) | ||
| 169 | if 0 in self._client_ids: | ||
| 170 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['workspaces', self._workspace_id, 'clients']) | ||
| 171 | r = self._query(url = url, method = 'GET') | ||
| 172 | if not r or not r.json(): | ||
| 173 | raise TogglAPIError(r) | ||
| 174 | |||
| 175 | billable_acc += sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=None), start=timedelta(milliseconds=0)) - sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=frozenset(map(lambda c: c['id'], r.json()))), start=timedelta(milliseconds=0)) | ||
| 176 | |||
| 177 | billable_acc += sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=frozenset(*(self._client_ids - {0}))), start=timedelta(milliseconds=0)) | ||
| 178 | |||
| 179 | return billable_acc | ||
| 180 | 148 | ||
| 181 | def get_running_clock(self, now=datetime.now(timezone.utc)): | 149 | def get_billable_hours(self, start_date: datetime, end_date: datetime = datetime.now(timezone.utc)) -> timedelta: |
| 182 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['me', 'time_entries', 'current']) | 150 | return sum(self.entry_durations(start_date, end_date=end_date), start=timedelta(milliseconds=0)) |
| 183 | r = self._query(url = url, method='GET') | ||
| 184 | 151 | ||
| 185 | if not r or (not r.json() and r.json() is not None): | 152 | def get_running_clock(self, now: datetime = datetime.now(timezone.utc)) -> timedelta | None: |
| 186 | raise TogglAPIError(r) | 153 | kimai_entries = self._session.get('/api/timesheets/active').json() |
| 187 | 154 | if not kimai_entries: | |
| 188 | if not r.json() or not r.json()['billable']: | ||
| 189 | return None | 155 | return None |
| 156 | entry = kimai_entries[0] | ||
| 190 | 157 | ||
| 191 | if self._client_ids is not None: | 158 | if entry['project']['customer']['id'] not in self._client_ids: |
| 192 | if 'pid' in r.json() and r.json()['pid']: | 159 | return None |
| 193 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['projects', str(r.json()['pid'])]) | ||
| 194 | pr = self._query(url = url, method = 'GET') | ||
| 195 | if not pr or not pr.json(): | ||
| 196 | raise TogglAPIError(pr) | ||
| 197 | |||
| 198 | if not pr.json(): | ||
| 199 | return None | ||
| 200 | |||
| 201 | if 'cid' in pr.json() and pr.json()['cid']: | ||
| 202 | if pr.json()['cid'] not in self._client_ids: | ||
| 203 | return None | ||
| 204 | elif 0 not in self._client_ids: | ||
| 205 | return None | ||
| 206 | elif 0 not in self._client_ids: | ||
| 207 | return None | ||
| 208 | |||
| 209 | start = isoparse(r.json()['start']) | ||
| 210 | 160 | ||
| 161 | start = isoparse(entry['begin']) | ||
| 211 | return now - start if start <= now else None | 162 | return now - start if start <= now else None |
| 212 | 163 | ||
| 213 | class Worktime(object): | 164 | class Worktime(object): |
| @@ -281,10 +232,10 @@ class Worktime(object): | |||
| 281 | 232 | ||
| 282 | config = Worktime.config() | 233 | config = Worktime.config() |
| 283 | config_dir = BaseDirectory.load_first_config('worktime') | 234 | config_dir = BaseDirectory.load_first_config('worktime') |
| 284 | api = TogglAPI( | 235 | api = KimaiAPI( |
| 285 | api_token=config.get("TOGGL", {}).get("ApiToken", None), | 236 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), |
| 286 | workspace_id=config.get("TOGGL", {}).get("Workspace", None), | 237 | api_token=config.get("KIMAI", {}).get("ApiToken", None), |
| 287 | client_ids=config.get("TOGGL", {}).get("ClientIds", None) | 238 | clients=config.get("KIMAI", {}).get("Clients", None) |
| 288 | ) | 239 | ) |
| 289 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') | 240 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') |
| 290 | 241 | ||
| @@ -496,7 +447,7 @@ class Worktime(object): | |||
| 496 | 447 | ||
| 497 | self.time_to_work += self.time_pulled_forward | 448 | self.time_to_work += self.time_pulled_forward |
| 498 | 449 | ||
| 499 | self.time_worked += api.get_billable_hours(self.start_date, self.now, rounding = config.get("WORKTIME", {}).get("rounding", True)) | 450 | self.time_worked += api.get_billable_hours(self.start_date, self.now) |
| 500 | 451 | ||
| 501 | def format_days(worktime, days, date_format=None): | 452 | def format_days(worktime, days, date_format=None): |
| 502 | if not date_format: | 453 | if not date_format: |
| @@ -886,7 +837,7 @@ def main(): | |||
| 886 | 837 | ||
| 887 | config = Worktime.config() | 838 | config = Worktime.config() |
| 888 | 839 | ||
| 889 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using toggl API') | 840 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using Kimai API') |
| 890 | parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = isotime, help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal())) | 841 | parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = isotime, help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal())) |
| 891 | parser.add_argument('--start', dest = 'start_datetime', metavar = 'TIME', type = isotime, help = 'Time to calculate status from (default: None)', default = None) | 842 | parser.add_argument('--start', dest = 'start_datetime', metavar = 'TIME', type = isotime, help = 'Time to calculate status from (default: None)', default = None) |
| 892 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') | 843 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') |
