taskai-cli 1.2.0__tar.gz → 1.3.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.
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/PKG-INFO +1 -1
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/pyproject.toml +1 -1
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/cli.py +25 -1
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/models.py +8 -1
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/views.py +10 -3
- taskai_cli-1.2.0/migrations/_001_removing_lists.py +0 -50
- taskai_cli-1.2.0/migrations/convert_title_to_name.py +0 -30
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/.gitignore +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/DEVLOG.md +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/README.md +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/docs/index.md +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/mkdocs.yml +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/scripts/bump_version.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/config.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/help_menu.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/json_dir_database.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/services/ai.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/services/pomodoro.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/services/repair_database.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/services/user_setup.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/test/test_cli.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/test/test_execution.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/test/test_json_dir_database.py +0 -0
- {taskai_cli-1.2.0 → taskai_cli-1.3.0}/test/test_view.py +0 -0
|
@@ -52,6 +52,12 @@ class Controller:
|
|
|
52
52
|
return db.get_item(id_)
|
|
53
53
|
return None
|
|
54
54
|
|
|
55
|
+
def _find_item_by_identifier(identifier: str|int) -> TodoItem:
|
|
56
|
+
if _is_int(identifier):
|
|
57
|
+
return db.get_item(identifier)
|
|
58
|
+
else:
|
|
59
|
+
return Controller._find_model_by_stringmatch("name", identifier)
|
|
60
|
+
|
|
55
61
|
def _parse_item_kwargs(kwargs):
|
|
56
62
|
for k, v in kwargs.copy().items():
|
|
57
63
|
if v is None:
|
|
@@ -240,6 +246,15 @@ class Controller:
|
|
|
240
246
|
db.update_item(parent.id, child_ids=new_child_ids)
|
|
241
247
|
db.commit()
|
|
242
248
|
|
|
249
|
+
def add_link(parent_id: int|str, child_id: int|str):
|
|
250
|
+
parent: TodoItem = Controller._find_item_by_identifier(parent_id)
|
|
251
|
+
child: TodoItem = Controller._find_item_by_identifier(child_id)
|
|
252
|
+
|
|
253
|
+
if child.id not in parent.linked_ids:
|
|
254
|
+
parent.linked_ids.append(child_id)
|
|
255
|
+
db.update_item(parent.id, linked_ids=parent.linked_ids)
|
|
256
|
+
db.commit()
|
|
257
|
+
|
|
243
258
|
|
|
244
259
|
# utilities
|
|
245
260
|
def _parse_arg_string(arg_string: str) -> list[str]:
|
|
@@ -376,6 +391,11 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
376
391
|
parent_identifier = args[1]
|
|
377
392
|
item_name = args[2]
|
|
378
393
|
Controller.create_item(item_name, parent_identifier, **kwargs)
|
|
394
|
+
|
|
395
|
+
case "link":
|
|
396
|
+
parent_identifier = args[1]
|
|
397
|
+
item_identifier = args[2]
|
|
398
|
+
Controller.add_link(parent_identifier, item_identifier)
|
|
379
399
|
|
|
380
400
|
case "complete" | "done":
|
|
381
401
|
match args[1]:
|
|
@@ -406,7 +426,11 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
406
426
|
# developer use
|
|
407
427
|
case "db":
|
|
408
428
|
import orjson as json
|
|
409
|
-
print_json(json.dumps(db.
|
|
429
|
+
print_json(json.dumps(db.get_item(int(args[1])).model_dump()).decode())
|
|
430
|
+
|
|
431
|
+
case "status":
|
|
432
|
+
item_id, status_val = args[1:3]
|
|
433
|
+
Controller.update_item(item_id, status=status_val)
|
|
410
434
|
|
|
411
435
|
case "exit" | "exit()" | "quit":
|
|
412
436
|
return 0
|
|
@@ -4,7 +4,7 @@ from typing import Optional
|
|
|
4
4
|
from datetime import datetime, timedelta
|
|
5
5
|
|
|
6
6
|
# external
|
|
7
|
-
from pydantic import Field, BaseModel
|
|
7
|
+
from pydantic import Field, BaseModel, field_validator
|
|
8
8
|
|
|
9
9
|
class Base(BaseModel):
|
|
10
10
|
pass
|
|
@@ -39,6 +39,7 @@ class TodoItem(Base):
|
|
|
39
39
|
due_by: Optional[datetime] = None
|
|
40
40
|
comment_ids: list[int] = Field(default_factory=list)
|
|
41
41
|
dependency_ids: list[int] = Field(default_factory=list)
|
|
42
|
+
linked_ids: list[int] = Field(default_factory=list)
|
|
42
43
|
child_ids: list[int] = Field(default_factory=list)
|
|
43
44
|
priority: int = 0
|
|
44
45
|
recurs_every: Optional[list[timedelta]] = None
|
|
@@ -46,6 +47,12 @@ class TodoItem(Base):
|
|
|
46
47
|
recur_keep_incomplete: bool = False
|
|
47
48
|
status: str = ""
|
|
48
49
|
|
|
50
|
+
@field_validator("status", mode="before")
|
|
51
|
+
def coerce_status(cls, value):
|
|
52
|
+
if not isinstance(value, str):
|
|
53
|
+
return ""
|
|
54
|
+
return value
|
|
55
|
+
|
|
49
56
|
|
|
50
57
|
class Comment(Base):
|
|
51
58
|
content: str
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# local
|
|
2
|
-
from taskai.json_dir_database import JsonDirectoryDatabase
|
|
2
|
+
from taskai.json_dir_database import JsonDirectoryDatabase, DatabaseError
|
|
3
3
|
from taskai.models import TodoItem, Comment
|
|
4
4
|
from taskai.config import config
|
|
5
5
|
|
|
@@ -66,20 +66,27 @@ def view_lists(
|
|
|
66
66
|
display_string = _wrap_string(display_string, "[strike]", "[/strike]", condition=item.completed)
|
|
67
67
|
return display_string
|
|
68
68
|
|
|
69
|
-
def _print_item(item: TodoItem, level: int):
|
|
69
|
+
def _print_item(item: TodoItem, level: int, prefix=""):
|
|
70
70
|
if not show_done and item.completed:
|
|
71
71
|
return
|
|
72
72
|
|
|
73
73
|
display_string = _render_display_string(item)
|
|
74
74
|
indent = "\t" * level
|
|
75
|
-
print(indent + display_string)
|
|
75
|
+
print(indent + prefix + display_string)
|
|
76
76
|
|
|
77
77
|
def _recursive_print(item_id: int, level: int):
|
|
78
78
|
item = db.get_item(item_id)
|
|
79
79
|
_print_item(item, level)
|
|
80
|
+
for linked_id in item.linked_ids:
|
|
81
|
+
try:
|
|
82
|
+
linked_item = db.get_item(linked_id)
|
|
83
|
+
except DatabaseError:
|
|
84
|
+
continue
|
|
85
|
+
_print_item(linked_item, level+1, prefix="-->")
|
|
80
86
|
for child_id in item.child_ids:
|
|
81
87
|
_recursive_print(child_id, level+1)
|
|
82
88
|
|
|
89
|
+
|
|
83
90
|
for root in roots:
|
|
84
91
|
_recursive_print(root, 0)
|
|
85
92
|
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import orjson as json
|
|
3
|
-
from taskai.models import TodoItem
|
|
4
|
-
|
|
5
|
-
def migrate(data):
|
|
6
|
-
if "TodoList" in data:
|
|
7
|
-
for k, record in data["TodoList"].items():
|
|
8
|
-
|
|
9
|
-
record["child_ids"] = record.pop("item_ids")
|
|
10
|
-
TodoItem(**record)
|
|
11
|
-
print(f"Converting {k} to Item")
|
|
12
|
-
data["TodoItem"][k] = record
|
|
13
|
-
data.pop("TodoList")
|
|
14
|
-
|
|
15
|
-
for k, record in data["TodoItem"].items():
|
|
16
|
-
|
|
17
|
-
if "title" in record:
|
|
18
|
-
print(f"converting title->name for {k}")
|
|
19
|
-
record["title"] = record["name"]
|
|
20
|
-
record.pop("title")
|
|
21
|
-
|
|
22
|
-
if "parent" in record:
|
|
23
|
-
print(f"converting parent->parent_id for {k}")
|
|
24
|
-
record["parent_id"] = record["parent"]
|
|
25
|
-
record.pop("parent")
|
|
26
|
-
|
|
27
|
-
if "child_ids" not in record:
|
|
28
|
-
print(f"Adding Child IDs {k}")
|
|
29
|
-
record["child_ids"] = []
|
|
30
|
-
|
|
31
|
-
try:
|
|
32
|
-
data["TodoItem"][k] = TodoItem(**record).model_dump()
|
|
33
|
-
except:
|
|
34
|
-
print("validation for {} failed".format(k))
|
|
35
|
-
import sys
|
|
36
|
-
sys.exit()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
arg_parser = argparse.ArgumentParser()
|
|
40
|
-
arg_parser.add_argument("path", help="path to taskai root")
|
|
41
|
-
args = arg_parser.parse_args()
|
|
42
|
-
|
|
43
|
-
with open(args.path, "rb") as f:
|
|
44
|
-
data = json.loads(f.read())
|
|
45
|
-
|
|
46
|
-
migrate(data)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
with open(args.path, "wb") as f:
|
|
50
|
-
f.write(json.dumps(data))
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import orjson as json
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def convert_name_to_name(path):
|
|
6
|
-
with open(path, "rb") as f:
|
|
7
|
-
data = json.loads(f.read())
|
|
8
|
-
|
|
9
|
-
for k, record in data["TodoItem"].items():
|
|
10
|
-
|
|
11
|
-
if "name" in record:
|
|
12
|
-
print(f"migrating {k}")
|
|
13
|
-
record["name"] = record["name"]
|
|
14
|
-
record.pop("name")
|
|
15
|
-
elif "name" in record:
|
|
16
|
-
print(f"validated {k}")
|
|
17
|
-
else:
|
|
18
|
-
print(f"{k} is corrupted")
|
|
19
|
-
|
|
20
|
-
with open(path, "wb") as f:
|
|
21
|
-
f.write(json.dumps(data))
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
arg_parser = argparse.ArgumentParser()
|
|
25
|
-
arg_parser.add_argument("path", help="path to taskai root")
|
|
26
|
-
args = arg_parser.parse_args()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
convert_name_to_name(args.path)
|
|
30
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|