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.
Files changed (25) hide show
  1. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/PKG-INFO +1 -1
  2. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/pyproject.toml +1 -1
  3. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/cli.py +25 -1
  4. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/models.py +8 -1
  5. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/views.py +10 -3
  6. taskai_cli-1.2.0/migrations/_001_removing_lists.py +0 -50
  7. taskai_cli-1.2.0/migrations/convert_title_to_name.py +0 -30
  8. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/.github/workflows/publish-to-pypi.yml +0 -0
  9. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/.gitignore +0 -0
  10. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/DEVLOG.md +0 -0
  11. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/README.md +0 -0
  12. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/docs/index.md +0 -0
  13. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/mkdocs.yml +0 -0
  14. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/scripts/bump_version.py +0 -0
  15. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/config.py +0 -0
  16. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/help_menu.py +0 -0
  17. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/json_dir_database.py +0 -0
  18. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/services/ai.py +0 -0
  19. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/services/pomodoro.py +0 -0
  20. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/services/repair_database.py +0 -0
  21. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/taskai/services/user_setup.py +0 -0
  22. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/test/test_cli.py +0 -0
  23. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/test/test_execution.py +0 -0
  24. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/test/test_json_dir_database.py +0 -0
  25. {taskai_cli-1.2.0 → taskai_cli-1.3.0}/test/test_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskai-cli
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Author-email: Alex Paskal <alexcpaskal@gmail.com>
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: google-genai>=2.8
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "taskai-cli"
3
- version = "1.2.0"
3
+ version = "1.3.0"
4
4
  description = ""
5
5
  authors = [{name = "Alex Paskal", email = "alexcpaskal@gmail.com"}]
6
6
  readme = "README.md"
@@ -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.read(args[1]).model_dump()).decode())
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