timedctl 3.3.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.3.0 → timedctl-3.5.0}/PKG-INFO +2 -2
- {timedctl-3.3.0 → timedctl-3.5.0}/pyproject.toml +2 -2
- timedctl-3.5.0/timedctl/http_cache.sqlite +0 -0
- {timedctl-3.3.0 → timedctl-3.5.0}/timedctl/timedctl.py +42 -34
- {timedctl-3.3.0 → timedctl-3.5.0}/LICENSE +0 -0
- {timedctl-3.3.0 → timedctl-3.5.0}/README.md +0 -0
- {timedctl-3.3.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,19 +485,24 @@ 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."""
|
|
491
|
+
if not timed.activities.current:
|
|
492
|
+
error_handler("ERR_NO_CURRENT_ACTIVITY")
|
|
493
|
+
else:
|
|
494
|
+
timed.activities.stop()
|
|
488
495
|
msg("Activity stopped successfully.")
|
|
489
496
|
|
|
490
497
|
|
|
491
|
-
@activity.command()
|
|
492
|
-
def show(
|
|
498
|
+
@activity.command(aliases=["s", "get", "info"])
|
|
499
|
+
def show():
|
|
493
500
|
"""Show current activity."""
|
|
494
501
|
current_activity = timed.activities.current
|
|
495
502
|
if current_activity:
|
|
496
503
|
msg(
|
|
497
|
-
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')})"
|
|
498
506
|
)
|
|
499
507
|
else:
|
|
500
508
|
error_handler("ERR_NO_CURRENT_ACTIVITY")
|
|
@@ -505,23 +513,23 @@ def generate_timesheet():
|
|
|
505
513
|
"""Generate the timesheet of the current activities."""
|
|
506
514
|
activities = timed.activities.get()
|
|
507
515
|
if activities:
|
|
508
|
-
for
|
|
509
|
-
if not
|
|
510
|
-
if not
|
|
511
|
-
|
|
512
|
-
from_time =
|
|
513
|
-
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"]
|
|
514
522
|
duration = to_time - from_time
|
|
515
|
-
task =
|
|
523
|
+
task = activity_obj["relationships"]["task"]["data"]["id"]
|
|
516
524
|
timed.reports.post(
|
|
517
525
|
{
|
|
518
526
|
"duration": duration,
|
|
519
|
-
"comment":
|
|
527
|
+
"comment": activity_obj["attributes"]["comment"],
|
|
520
528
|
},
|
|
521
529
|
{"task": task},
|
|
522
530
|
)
|
|
523
531
|
timed.activities.patch(
|
|
524
|
-
|
|
532
|
+
activity_obj["id"], {"transferred": True}, {"task": task}
|
|
525
533
|
)
|
|
526
534
|
msg("Timesheet generated successfully.")
|
|
527
535
|
else:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|