timedctl 3.4.0__tar.gz → 3.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {timedctl-3.4.0 → timedctl-3.5.0}/PKG-INFO +2 -2
- {timedctl-3.4.0 → timedctl-3.5.0}/pyproject.toml +2 -2
- timedctl-3.5.0/timedctl/http_cache.sqlite +0 -0
- {timedctl-3.4.0 → timedctl-3.5.0}/timedctl/timedctl.py +38 -34
- {timedctl-3.4.0 → timedctl-3.5.0}/LICENSE +0 -0
- {timedctl-3.4.0 → timedctl-3.5.0}/README.md +0 -0
- {timedctl-3.4.0 → timedctl-3.5.0}/timedctl/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: timedctl
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.5.0
|
|
4
4
|
Summary: CLI for timed
|
|
5
5
|
License: AGPL-3.0-only
|
|
6
6
|
Author: Arthur Deierlein
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Requires-Dist: click (>=8.1.3,<9.0.0)
|
|
14
14
|
Requires-Dist: click-aliases (>=1.0.1,<2.0.0)
|
|
15
|
-
Requires-Dist: libtimed (>=0.
|
|
15
|
+
Requires-Dist: libtimed (>=0.3.0,<0.4.0)
|
|
16
16
|
Requires-Dist: pyfzf (>=0.3.1,<0.4.0)
|
|
17
17
|
Requires-Dist: rich (>=13.4.2,<14.0.0)
|
|
18
18
|
Requires-Dist: terminaltables (>=3.1.10,<4.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "timedctl"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.5.0"
|
|
4
4
|
description = "CLI for timed"
|
|
5
5
|
authors = ["Arthur Deierlein <arthur.deierlein@adfinis.com>", "Gian Klug <gian.klug@adfinis.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -11,7 +11,7 @@ python = "^3.10"
|
|
|
11
11
|
click = "^8.1.3"
|
|
12
12
|
pyfzf = "^0.3.1"
|
|
13
13
|
rich = "^13.4.2"
|
|
14
|
-
libtimed = "^0.
|
|
14
|
+
libtimed = "^0.3.0"
|
|
15
15
|
terminaltables = "^3.1.10"
|
|
16
16
|
tomlkit = "^0.11.8"
|
|
17
17
|
click-aliases = "^1.0.1"
|
|
Binary file
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"""CLI application for libtimed."""
|
|
3
3
|
|
|
4
4
|
import datetime
|
|
5
|
-
import tomllib
|
|
6
5
|
import os
|
|
7
6
|
import re
|
|
8
7
|
import sys
|
|
@@ -11,10 +10,11 @@ import click
|
|
|
11
10
|
import pyfzf
|
|
12
11
|
import rich
|
|
13
12
|
import terminaltables
|
|
13
|
+
import tomllib
|
|
14
14
|
from click_aliases import ClickAliasedGroup
|
|
15
|
-
from tomlkit import dump
|
|
16
15
|
from libtimed import TimedAPIClient
|
|
17
16
|
from libtimed.oidc import OIDCClient
|
|
17
|
+
from tomlkit import dump
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def load_config():
|
|
@@ -204,11 +204,12 @@ def get_reports(date):
|
|
|
204
204
|
task_obj = report["relationships"]["task"]
|
|
205
205
|
task = task_obj["attributes"]["name"]
|
|
206
206
|
|
|
207
|
-
project_obj = timed.projects.get(id=task_obj["relationships"]["project"]["id"])
|
|
207
|
+
project_obj = timed.projects.get(id=task_obj["relationships"]["project"]["id"], cached=True)
|
|
208
208
|
project = project_obj["attributes"]["name"]
|
|
209
209
|
|
|
210
210
|
customer_obj = timed.customers.get(
|
|
211
|
-
id=project_obj["relationships"]["customer"]["data"]["id"]
|
|
211
|
+
id=project_obj["relationships"]["customer"]["data"]["id"],
|
|
212
|
+
cached=True
|
|
212
213
|
)
|
|
213
214
|
customer = customer_obj["attributes"]["name"]
|
|
214
215
|
|
|
@@ -232,28 +233,30 @@ def get_reports(date):
|
|
|
232
233
|
def get_activities(date):
|
|
233
234
|
"""Get activities."""
|
|
234
235
|
activities = timed.activities.get(
|
|
235
|
-
{"day": date},
|
|
236
|
+
{"day": date},
|
|
237
|
+
include="task,task.project,task.project.customer",
|
|
236
238
|
)
|
|
237
239
|
table = [["Activity", "Comment", "Start", "End"]]
|
|
238
|
-
for
|
|
239
|
-
task_obj =
|
|
240
|
+
for activity_obj in activities:
|
|
241
|
+
task_obj = activity_obj["relationships"]["task"]
|
|
240
242
|
task = task_obj["attributes"]["name"]
|
|
241
243
|
|
|
242
|
-
project_obj = timed.projects.get(id=task_obj["relationships"]["project"]["id"])
|
|
244
|
+
project_obj = timed.projects.get(id=task_obj["relationships"]["project"]["id"], cached=True)
|
|
243
245
|
project = project_obj["attributes"]["name"]
|
|
244
246
|
|
|
245
247
|
customer_obj = timed.customers.get(
|
|
246
|
-
id=project_obj["relationships"]["customer"]["data"]["id"]
|
|
248
|
+
id=project_obj["relationships"]["customer"]["data"]["id"],
|
|
249
|
+
cached=True
|
|
247
250
|
)
|
|
248
251
|
customer = customer_obj["attributes"]["name"]
|
|
249
252
|
|
|
250
253
|
table.append(
|
|
251
254
|
[
|
|
252
255
|
f"{customer} > {project} > {task}",
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if
|
|
256
|
+
activity_obj["attributes"]["comment"],
|
|
257
|
+
activity_obj["attributes"]["from-time"].strftime("%H:%M:%S"),
|
|
258
|
+
activity_obj["attributes"]["to-time"].strftime("%H:%M:%S")
|
|
259
|
+
if activity_obj["attributes"]["to-time"] is not None
|
|
257
260
|
else "active",
|
|
258
261
|
]
|
|
259
262
|
)
|
|
@@ -315,9 +318,9 @@ def add():
|
|
|
315
318
|
@click.option("--task", default=None)
|
|
316
319
|
@click.option("--description", default=None)
|
|
317
320
|
@click.option("--duration", default=None)
|
|
318
|
-
def add_report(customer, project, task, description, duration):
|
|
321
|
+
def add_report(customer, project, task, description, duration): # pylint: disable=R0912
|
|
319
322
|
"""Add report(s)."""
|
|
320
|
-
customers = timed.customers.get()
|
|
323
|
+
customers = timed.customers.get(cached=True)
|
|
321
324
|
# ask the user to select a customer
|
|
322
325
|
msg("Select a customer")
|
|
323
326
|
# select a customer
|
|
@@ -329,7 +332,7 @@ def add_report(customer, project, task, description, duration):
|
|
|
329
332
|
else:
|
|
330
333
|
customer = fzf_wrapper(customers, ["attributes", "name"], "Select a customer: ")
|
|
331
334
|
# get projects
|
|
332
|
-
projects = timed.projects.get({"customer": customer["id"]})
|
|
335
|
+
projects = timed.projects.get({"customer": customer["id"]}, cached=True)
|
|
333
336
|
# select a project
|
|
334
337
|
if project:
|
|
335
338
|
project = [p for p in projects if p["attributes"]["name"] == project]
|
|
@@ -339,7 +342,7 @@ def add_report(customer, project, task, description, duration):
|
|
|
339
342
|
else:
|
|
340
343
|
project = fzf_wrapper(projects, ["attributes", "name"], "Select a project: ")
|
|
341
344
|
# get tasks
|
|
342
|
-
tasks = timed.tasks.get({"project": project["id"]})
|
|
345
|
+
tasks = timed.tasks.get({"project": project["id"]}, cached=True)
|
|
343
346
|
# select a task
|
|
344
347
|
if task:
|
|
345
348
|
task = [t for t in tasks if t["attributes"]["name"] == task]
|
|
@@ -439,7 +442,7 @@ def activity():
|
|
|
439
442
|
@click.option("--task", default=None)
|
|
440
443
|
def start(comment, customer, project, task):
|
|
441
444
|
"""Start recording activity."""
|
|
442
|
-
customers = timed.customers.get()
|
|
445
|
+
customers = timed.customers.get(cached=True)
|
|
443
446
|
# ask the user to select a customer
|
|
444
447
|
msg("Select a customer")
|
|
445
448
|
# select a customer
|
|
@@ -451,7 +454,7 @@ def start(comment, customer, project, task):
|
|
|
451
454
|
else:
|
|
452
455
|
customer = fzf_wrapper(customers, ["attributes", "name"], "Select a customer: ")
|
|
453
456
|
# get projects
|
|
454
|
-
projects = timed.projects.get({"customer": customer["id"]})
|
|
457
|
+
projects = timed.projects.get({"customer": customer["id"]}, cached=True)
|
|
455
458
|
# select a project
|
|
456
459
|
if project:
|
|
457
460
|
project = [p for p in projects if p["attributes"]["name"] == project]
|
|
@@ -461,7 +464,7 @@ def start(comment, customer, project, task):
|
|
|
461
464
|
else:
|
|
462
465
|
project = fzf_wrapper(projects, ["attributes", "name"], "Select a project: ")
|
|
463
466
|
# get tasks
|
|
464
|
-
tasks = timed.tasks.get({"project": project["id"]})
|
|
467
|
+
tasks = timed.tasks.get({"project": project["id"]}, cached=True)
|
|
465
468
|
# select a task
|
|
466
469
|
if task:
|
|
467
470
|
task = [t for t in tasks if t["attributes"]["name"] == task]
|
|
@@ -482,8 +485,8 @@ def start(comment, customer, project, task):
|
|
|
482
485
|
error_handler("ERR_ACTIVITY_START_FAILED")
|
|
483
486
|
|
|
484
487
|
|
|
485
|
-
@activity.command()
|
|
486
|
-
def stop(
|
|
488
|
+
@activity.command(aliases=["end", "finish"])
|
|
489
|
+
def stop():
|
|
487
490
|
"""Stop current activity."""
|
|
488
491
|
if not timed.activities.current:
|
|
489
492
|
error_handler("ERR_NO_CURRENT_ACTIVITY")
|
|
@@ -492,13 +495,14 @@ def stop(aliases=["end", "finish"]):
|
|
|
492
495
|
msg("Activity stopped successfully.")
|
|
493
496
|
|
|
494
497
|
|
|
495
|
-
@activity.command()
|
|
496
|
-
def show(
|
|
498
|
+
@activity.command(aliases=["s", "get", "info"])
|
|
499
|
+
def show():
|
|
497
500
|
"""Show current activity."""
|
|
498
501
|
current_activity = timed.activities.current
|
|
499
502
|
if current_activity:
|
|
500
503
|
msg(
|
|
501
|
-
f"Current activity: {current_activity['attributes']['comment']} (Since
|
|
504
|
+
f"Current activity: {current_activity['attributes']['comment']} (Since "
|
|
505
|
+
+ f"{current_activity['attributes']['from-time'].strftime('%H:%M:%S')})"
|
|
502
506
|
)
|
|
503
507
|
else:
|
|
504
508
|
error_handler("ERR_NO_CURRENT_ACTIVITY")
|
|
@@ -509,23 +513,23 @@ def generate_timesheet():
|
|
|
509
513
|
"""Generate the timesheet of the current activities."""
|
|
510
514
|
activities = timed.activities.get()
|
|
511
515
|
if activities:
|
|
512
|
-
for
|
|
513
|
-
if not
|
|
514
|
-
if not
|
|
515
|
-
|
|
516
|
-
from_time =
|
|
517
|
-
to_time =
|
|
516
|
+
for activity_obj in activities:
|
|
517
|
+
if not activity_obj["attributes"]["transferred"]:
|
|
518
|
+
if not activity_obj["attributes"]["to-time"]:
|
|
519
|
+
activity_obj["attributes"]["to-time"] = datetime.datetime.now()
|
|
520
|
+
from_time = activity_obj["attributes"]["from-time"]
|
|
521
|
+
to_time = activity_obj["attributes"]["to-time"]
|
|
518
522
|
duration = to_time - from_time
|
|
519
|
-
task =
|
|
523
|
+
task = activity_obj["relationships"]["task"]["data"]["id"]
|
|
520
524
|
timed.reports.post(
|
|
521
525
|
{
|
|
522
526
|
"duration": duration,
|
|
523
|
-
"comment":
|
|
527
|
+
"comment": activity_obj["attributes"]["comment"],
|
|
524
528
|
},
|
|
525
529
|
{"task": task},
|
|
526
530
|
)
|
|
527
531
|
timed.activities.patch(
|
|
528
|
-
|
|
532
|
+
activity_obj["id"], {"transferred": True}, {"task": task}
|
|
529
533
|
)
|
|
530
534
|
msg("Timesheet generated successfully.")
|
|
531
535
|
else:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|