taskai-cli 0.1.5__tar.gz → 1.0.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-0.1.5 → taskai_cli-1.0.0}/DEVLOG.md +84 -1
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/PKG-INFO +4 -4
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/README.md +3 -3
- taskai_cli-1.0.0/migrations/convert_title_to_name.py +30 -0
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/pyproject.toml +1 -1
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/taskai/cli.py +102 -140
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/taskai/help_menu.py +3 -3
- taskai_cli-1.0.0/taskai/json_dir_database.py +252 -0
- taskai_cli-1.0.0/taskai/models.py +64 -0
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/taskai/services/ai.py +19 -12
- taskai_cli-1.0.0/taskai/services/repair_database.py +33 -0
- taskai_cli-1.0.0/taskai/views.py +104 -0
- taskai_cli-1.0.0/test/test_cli.py +64 -0
- taskai_cli-1.0.0/test/test_execution.py +12 -0
- taskai_cli-1.0.0/test/test_json_dir_database.py +97 -0
- taskai_cli-1.0.0/test/test_view.py +60 -0
- taskai_cli-0.1.5/taskai/json_dir_database.py +0 -204
- taskai_cli-0.1.5/taskai/models.py +0 -63
- taskai_cli-0.1.5/taskai/services/repair_database.py +0 -39
- taskai_cli-0.1.5/taskai/views.py +0 -94
- taskai_cli-0.1.5/test/test_json_dir_database.py +0 -80
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/.gitignore +0 -0
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/docs/index.md +0 -0
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/mkdocs.yml +0 -0
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/taskai/config.py +0 -0
- {taskai_cli-0.1.5 → taskai_cli-1.0.0}/taskai/services/user_setup.py +0 -0
|
@@ -1,3 +1,86 @@
|
|
|
1
|
+
# 6-20
|
|
2
|
+
|
|
3
|
+
Let's think critically about what i want this API to lookk like. How should be people be
|
|
4
|
+
using the CLI?
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
task create {name} {**kwargs} -> create an item
|
|
8
|
+
task add {parent_id} ... -> create an item as a child
|
|
9
|
+
task show all
|
|
10
|
+
task show {id}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 6-19
|
|
15
|
+
|
|
16
|
+
Really this whole thing should just be a tree abstraction, and I should have a root node class
|
|
17
|
+
and just built a tree database with some attrs depending on the type
|
|
18
|
+
|
|
19
|
+
idk why i'm being a dipshit
|
|
20
|
+
But that's gonna be the next iteration
|
|
21
|
+
|
|
22
|
+
# 6-18
|
|
23
|
+
|
|
24
|
+
Everything is fucked up anyways lol so might as qweell make some decisions about
|
|
25
|
+
the best way to do the database
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
class DB:
|
|
31
|
+
|
|
32
|
+
def get_item(id: int) -> TodoItem:
|
|
33
|
+
def get_comment(id: int) -> Comment:
|
|
34
|
+
def get_config() -> CLIConfig:
|
|
35
|
+
def create_item(name: str, parent: Optional[TodoItem]=None, ...) -> str:
|
|
36
|
+
def create_comment(content: str, parent: TodoItem) -> str:
|
|
37
|
+
def delete_item(name: str) -> bool:
|
|
38
|
+
def delete_comment(name: str) -> bool:
|
|
39
|
+
def update_item(id_: int, kwargs) -> bool:
|
|
40
|
+
def update_comment(id_: int, kwargs) -> bool:
|
|
41
|
+
def update_config(kwargs) -> bool:
|
|
42
|
+
|
|
43
|
+
def connect():
|
|
44
|
+
pass
|
|
45
|
+
def commit():
|
|
46
|
+
pass
|
|
47
|
+
def validate():
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Important factors:
|
|
53
|
+
- do i want to serialize/deserialize every record twice? probably not
|
|
54
|
+
- so let's make sure that everything is read-only
|
|
55
|
+
|
|
56
|
+
How do i want to handle parentage?
|
|
57
|
+
i could:
|
|
58
|
+
- do it at the client level i.e. make sure to call (add parent)
|
|
59
|
+
- do it at the db level i.e. on every create, update, and delete method, validate
|
|
60
|
+
|
|
61
|
+
Let's do it at the db level - i want the database to be responsible for ensuring data
|
|
62
|
+
validation so I don't have to worry about it when I'm writing code downstream
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# 6-17
|
|
66
|
+
|
|
67
|
+
Alright I find myself needing to make some design decisions about hierarchical lists.
|
|
68
|
+
|
|
69
|
+
I could:
|
|
70
|
+
- differentiate between child items and child lists, and have items only be leaf nodes
|
|
71
|
+
pros:
|
|
72
|
+
cons:
|
|
73
|
+
- treat everything as a single "item" and simply due away with the concept of lists as a
|
|
74
|
+
separate data point
|
|
75
|
+
- still distinguish between the two but treat them all as child ids - useful for sorting
|
|
76
|
+
|
|
77
|
+
Looking at it, i see no good reason to distinguish between items and lists - it complicates the
|
|
78
|
+
code without adding any additional functionality. There is nothing that a list does that an item
|
|
79
|
+
can't do apart from be a container, and there's no reason why an item can't also be a container.
|
|
80
|
+
|
|
81
|
+
So let's just go ahead and implement that change.
|
|
82
|
+
|
|
83
|
+
|
|
1
84
|
# 6-14
|
|
2
85
|
|
|
3
86
|
Damn it's been a productive couple of weeks. App is deployed on PyPi and I'm starting to think
|
|
@@ -28,7 +111,7 @@ put would be as methods on the objects, i.e. "item.updateList()", etc.
|
|
|
28
111
|
Alright so how are comments gonna look here:
|
|
29
112
|
|
|
30
113
|
```
|
|
31
|
-
|
|
114
|
+
Name:
|
|
32
115
|
Due By:
|
|
33
116
|
Description:
|
|
34
117
|
Depends On:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: taskai-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Author-email: Alex Paskal <alexcpaskal@gmail.com>
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: google-genai>=2.8
|
|
@@ -15,12 +15,12 @@ Welcome to Task AI - a command-line todo list with some extra ai features.
|
|
|
15
15
|
|
|
16
16
|
Here's what you can do:
|
|
17
17
|
|
|
18
|
-
- `task show all` --> show all of your lists and item
|
|
18
|
+
- `task show all` --> show all of your lists and item names, with their respective IDs prepended
|
|
19
19
|
- `task show {id or substring}` --> find the list or item matching your identifier and show it using its respective type's show command
|
|
20
|
-
- `task show list {id or substring}` --> show the list and all of its item
|
|
20
|
+
- `task show list {id or substring}` --> show the list and all of its item names
|
|
21
21
|
- `task show item {id}` --> show the associated item and all of its specified information
|
|
22
22
|
- `task show items {id1},{id2},...,{idx}` --> show the associated items and all of their specified information
|
|
23
|
-
- `task create item {list id or substring} {
|
|
23
|
+
- `task create item {list id or substring} {name} {**kwargs}` --> Create a new item for the associated list. Can specify kwargs as --optional cli arguments.
|
|
24
24
|
- `task create list {name}` --> create a new list by that name
|
|
25
25
|
- `task delete {id}` --> deletes the list or item associated with that id
|
|
26
26
|
- `task delete item {id}` --> deletes the item associated with that id
|
|
@@ -4,12 +4,12 @@ Welcome to Task AI - a command-line todo list with some extra ai features.
|
|
|
4
4
|
|
|
5
5
|
Here's what you can do:
|
|
6
6
|
|
|
7
|
-
- `task show all` --> show all of your lists and item
|
|
7
|
+
- `task show all` --> show all of your lists and item names, with their respective IDs prepended
|
|
8
8
|
- `task show {id or substring}` --> find the list or item matching your identifier and show it using its respective type's show command
|
|
9
|
-
- `task show list {id or substring}` --> show the list and all of its item
|
|
9
|
+
- `task show list {id or substring}` --> show the list and all of its item names
|
|
10
10
|
- `task show item {id}` --> show the associated item and all of its specified information
|
|
11
11
|
- `task show items {id1},{id2},...,{idx}` --> show the associated items and all of their specified information
|
|
12
|
-
- `task create item {list id or substring} {
|
|
12
|
+
- `task create item {list id or substring} {name} {**kwargs}` --> Create a new item for the associated list. Can specify kwargs as --optional cli arguments.
|
|
13
13
|
- `task create list {name}` --> create a new list by that name
|
|
14
14
|
- `task delete {id}` --> deletes the list or item associated with that id
|
|
15
15
|
- `task delete item {id}` --> deletes the item associated with that id
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
|
|
@@ -6,11 +6,12 @@ import builtins
|
|
|
6
6
|
import fnmatch
|
|
7
7
|
import sys
|
|
8
8
|
import subprocess
|
|
9
|
+
import getpass
|
|
9
10
|
|
|
10
11
|
# local
|
|
11
12
|
from taskai.json_dir_database import JsonDirectoryDatabase
|
|
12
13
|
from taskai.views import view_lists, view_item, view_items
|
|
13
|
-
from taskai.models import
|
|
14
|
+
from taskai.models import TodoItem, Comment
|
|
14
15
|
from taskai.services.ai import ai_headstart_service, ai_natural_language_service
|
|
15
16
|
from taskai.services.user_setup import user_setup_service
|
|
16
17
|
from taskai.services.repair_database import repair_database_service
|
|
@@ -25,39 +26,46 @@ from rich.prompt import Prompt
|
|
|
25
26
|
|
|
26
27
|
# config
|
|
27
28
|
DB_PATH = ".taskai/task_db"
|
|
28
|
-
USER = os.getenv("USER")
|
|
29
|
+
USER = os.getenv("USER") if sys.platform == "linux" else os.getenv('USERNAME')
|
|
29
30
|
db = JsonDirectoryDatabase(
|
|
30
31
|
DB_PATH,
|
|
31
|
-
USER
|
|
32
|
+
USER,
|
|
32
33
|
)
|
|
33
34
|
db.connect()
|
|
34
|
-
GlobalConfig.load_dict(db.
|
|
35
|
-
|
|
36
|
-
og_help = builtins.help
|
|
37
|
-
def new_help(*args, **kwargs):
|
|
38
|
-
print(f"help: args: {args}, kwargs: {kwargs}")
|
|
39
|
-
return og_help(*args, **kwargs)
|
|
40
|
-
builtins.help = new_help
|
|
35
|
+
GlobalConfig.load_dict(db.get_config())
|
|
41
36
|
|
|
42
37
|
class Controller:
|
|
43
38
|
|
|
44
39
|
# utilities
|
|
45
|
-
def _find_model_by_stringmatch(attr: str, pattern: str) -> TodoItem|
|
|
40
|
+
def _find_model_by_stringmatch(attr: str, pattern: str) -> TodoItem|Comment|None:
|
|
46
41
|
|
|
47
42
|
for record_type in [
|
|
48
|
-
TodoList,
|
|
49
43
|
TodoItem,
|
|
50
44
|
Comment
|
|
51
45
|
]:
|
|
52
|
-
batch_attrs = db.
|
|
46
|
+
batch_attrs = db.get_item_batch_attr(attr)
|
|
53
47
|
inside_out = {v: k for k, v in batch_attrs.items()} # TODO this is hacky
|
|
54
48
|
results = fnmatch.filter(batch_attrs.values(), pattern)
|
|
55
49
|
if results:
|
|
56
50
|
id_ = inside_out[results[0]] # might be duplication
|
|
57
|
-
return db.
|
|
58
|
-
|
|
51
|
+
return db.get_item(id_)
|
|
59
52
|
return None
|
|
60
53
|
|
|
54
|
+
def _parse_item_kwargs(kwargs):
|
|
55
|
+
for k, v in kwargs.copy().items():
|
|
56
|
+
if v is None:
|
|
57
|
+
continue
|
|
58
|
+
match k:
|
|
59
|
+
case "completed": kwargs["completed"] = bool(v)
|
|
60
|
+
case "due_by": kwargs["due_by"] = datetime.strptime(v, "%m-%d-%Y")
|
|
61
|
+
case "depends_on": kwargs["dependency_ids"] = v.split(",")
|
|
62
|
+
return kwargs
|
|
63
|
+
|
|
64
|
+
def _get_root_ids():
|
|
65
|
+
return [
|
|
66
|
+
item_id for item_id in db.get_item_ids()
|
|
67
|
+
if db.get_item_attr(item_id, "parent_id") is None
|
|
68
|
+
]
|
|
61
69
|
|
|
62
70
|
def _debug(args, kwargs):
|
|
63
71
|
print("args:", args)
|
|
@@ -65,113 +73,69 @@ class Controller:
|
|
|
65
73
|
|
|
66
74
|
# CRUD
|
|
67
75
|
def show_all(show_done=True):
|
|
68
|
-
view_lists(db,
|
|
69
|
-
|
|
70
|
-
def
|
|
71
|
-
if id_ in db.items:
|
|
72
|
-
Controller.show_item(id_)
|
|
73
|
-
elif id_ in db.lists:
|
|
74
|
-
Controller.show_list(id_, show_done=show_done)
|
|
75
|
-
|
|
76
|
-
def show_by_list_name(value: str, show_done=True):
|
|
76
|
+
view_lists(db, Controller._get_root_ids(), show_done=show_done)
|
|
77
|
+
|
|
78
|
+
def show_by_item_name(value: str, show_done=True):
|
|
77
79
|
model = Controller._find_model_by_stringmatch("name", value)
|
|
78
|
-
if
|
|
79
|
-
Controller.
|
|
80
|
+
if model:
|
|
81
|
+
Controller.show_item(model.id, show_done=show_done)
|
|
80
82
|
else:
|
|
81
|
-
print(f"Could not find
|
|
82
|
-
|
|
83
|
-
def show_list(list_id: int|str, show_done=True):
|
|
84
|
-
view_lists(db, [list_id], show_done=show_done)
|
|
85
|
-
|
|
86
|
-
def show_lists():
|
|
87
|
-
view_lists(db, db.lists.keys(), show_items=False)
|
|
83
|
+
print(f"Could not find item matching pattern '{value}'")
|
|
88
84
|
|
|
89
|
-
def show_item(item_id: int
|
|
90
|
-
view_item(db, item_id)
|
|
85
|
+
def show_item(item_id: int, **kwargs):
|
|
86
|
+
view_item(db, item_id, **kwargs)
|
|
91
87
|
|
|
92
|
-
def show_items(item_ids: str):
|
|
88
|
+
def show_items(item_ids: str, **kwargs):
|
|
93
89
|
item_ids = item_ids.split(",")
|
|
94
|
-
view_items(db, item_ids)
|
|
90
|
+
view_items(db, item_ids, **kwargs)
|
|
95
91
|
|
|
96
92
|
def show_examples():
|
|
97
93
|
...
|
|
98
94
|
print("Not implemented yet")
|
|
99
95
|
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
# TODO this should be just lists, not models
|
|
112
|
-
list_id = list_.id
|
|
113
|
-
|
|
114
|
-
item = TodoItem(title=title, list_id=list_id)
|
|
115
|
-
|
|
116
|
-
for k, v in kwargs.items():
|
|
117
|
-
if v is None:
|
|
118
|
-
continue
|
|
119
|
-
match k:
|
|
120
|
-
case "completed": item.completed = bool(v)
|
|
121
|
-
case "description": item.description = str(v)
|
|
122
|
-
case "due_by": item.due_by = datetime.strptime(v, "%m-%d-%Y")
|
|
123
|
-
case "parent": item.parent = str(v)
|
|
124
|
-
case "priority": item.priority = int(v)
|
|
125
|
-
case "depends_on": item.dependency_ids.extend(v.split(","))
|
|
126
|
-
# TODO handle recurrence
|
|
127
|
-
db.create(item)
|
|
96
|
+
def create_item(name: str, parent_id=None, **kwargs):
|
|
97
|
+
if parent_id is not None and not _is_int(parent_id):
|
|
98
|
+
parent = Controller._find_model_by_stringmatch("name", parent_id)
|
|
99
|
+
if not parent:
|
|
100
|
+
Controller.throw_error(f"Could not find parent by id '{parent_id}'")
|
|
101
|
+
return
|
|
102
|
+
parent_id = parent.id
|
|
103
|
+
|
|
104
|
+
kwargs = Controller._parse_item_kwargs(kwargs)
|
|
105
|
+
item_id = db.create_item(name=name, parent_id=parent_id, **kwargs)
|
|
106
|
+
print(f"Created item {item_id} - '{name}'")
|
|
128
107
|
db.commit()
|
|
129
108
|
|
|
130
109
|
def create_comment(item_id: int|str, content: str):
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
content=content
|
|
134
|
-
)
|
|
135
|
-
db.create(comment)
|
|
110
|
+
comment_id = db.create_comment(content=content, item_id=item_id)
|
|
111
|
+
print(f"Added comment {comment_id} to item {item_id} - '{content}'")
|
|
136
112
|
db.commit()
|
|
137
113
|
|
|
138
114
|
def update_item(item_id: int|str, **kwargs):
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
match k:
|
|
144
|
-
case "title": item.title = str(v)
|
|
145
|
-
case "list_id": item.list_id = str(v)
|
|
146
|
-
case "completed": item.completed = bool(v)
|
|
147
|
-
case "description": item.description = str(v)
|
|
148
|
-
case "due_by": item.due_by = datetime.strptime(v, "%m-%d-%Y")
|
|
149
|
-
case "parent": item.parent = str(v)
|
|
150
|
-
case "priority": item.priority = int(v)
|
|
151
|
-
# TODO handle recurrence
|
|
152
|
-
|
|
153
|
-
db.update(item)
|
|
115
|
+
if not _is_int(item_id):
|
|
116
|
+
item_id = Controller._find_model_by_stringmatch("name", item_id)
|
|
117
|
+
db.update_item(item_id, **kwargs)
|
|
118
|
+
print(f"Updated item {item_id}")
|
|
154
119
|
db.commit()
|
|
155
120
|
|
|
156
|
-
def
|
|
157
|
-
db.
|
|
121
|
+
def delete_item(id_: int|str):
|
|
122
|
+
db.delete_item(id_)
|
|
158
123
|
db.commit()
|
|
159
|
-
print(f"Deleted {id_}")
|
|
124
|
+
print(f"Deleted item {id_}")
|
|
160
125
|
|
|
161
|
-
def
|
|
162
|
-
|
|
163
|
-
if
|
|
164
|
-
db.
|
|
126
|
+
def delete_item_by_name(name: str):
|
|
127
|
+
item = Controller._find_model_by_stringmatch("name", name)
|
|
128
|
+
if item:
|
|
129
|
+
db.delete_item(item.id)
|
|
165
130
|
db.commit()
|
|
166
131
|
else:
|
|
167
132
|
Controller.throw_error("Cannot find list by name")
|
|
168
|
-
|
|
169
133
|
|
|
170
134
|
def delete_completed():
|
|
171
|
-
for item_id in db.
|
|
172
|
-
item: TodoItem = db.
|
|
135
|
+
for item_id in db.get_item_ids():
|
|
136
|
+
item: TodoItem = db.get_item(item_id)
|
|
173
137
|
if item.completed:
|
|
174
|
-
db.
|
|
138
|
+
db.delete_item(item_id)
|
|
175
139
|
db.commit()
|
|
176
140
|
|
|
177
141
|
def ai_headstart(item_id: int|str):
|
|
@@ -185,21 +149,23 @@ class Controller:
|
|
|
185
149
|
|
|
186
150
|
def throw_error(error_description: str, *args, **kwargs):
|
|
187
151
|
print(f"[red]ERROR: {error_description}[/red]\nargs={args}\nkwargs={kwargs}")
|
|
152
|
+
import sys
|
|
153
|
+
sys.exit(-1)
|
|
188
154
|
|
|
189
155
|
def get_config_value(key: str):
|
|
190
|
-
print(db.
|
|
156
|
+
print(getattr(db.get_config(), key))
|
|
191
157
|
|
|
192
158
|
def list_config():
|
|
193
159
|
for k, v in db.get_config().model_dump().items():
|
|
194
160
|
print(f"{k}={v}")
|
|
195
161
|
|
|
196
162
|
def set_config_value(key: str, value: any):
|
|
197
|
-
db.
|
|
163
|
+
db.update_config(**{key: value})
|
|
198
164
|
db.commit()
|
|
199
165
|
print(f"setting {key}={value}")
|
|
200
166
|
|
|
201
167
|
def remove_config_value(key: str):
|
|
202
|
-
db.
|
|
168
|
+
db.update_config(**{key: None})
|
|
203
169
|
db.commit()
|
|
204
170
|
|
|
205
171
|
def run_setup_service():
|
|
@@ -208,30 +174,37 @@ class Controller:
|
|
|
208
174
|
def repair_service():
|
|
209
175
|
repair_database_service(db)
|
|
210
176
|
|
|
211
|
-
def move_item(item_id: int|str,
|
|
212
|
-
|
|
213
|
-
if item_id not in db.items:
|
|
214
|
-
Controller.throw_error(f"Couldn't find items with id {item_id}")
|
|
215
|
-
item: TodoItem = db.read(item_id)
|
|
177
|
+
def move_item(item_id: int|str, parent_identifier: int|str):
|
|
216
178
|
|
|
217
|
-
if _is_int(
|
|
218
|
-
|
|
179
|
+
if _is_int(item_id):
|
|
180
|
+
item = db.get_item(item_id)
|
|
219
181
|
else:
|
|
220
|
-
|
|
221
|
-
if not new_list_:
|
|
222
|
-
Controller.throw_error(f"Couldn't locate list by identifier {list_identifier}")
|
|
182
|
+
item = Controller._find_model_by_stringmatch("name", item_id)
|
|
223
183
|
|
|
224
|
-
#
|
|
225
|
-
item.
|
|
226
|
-
|
|
184
|
+
# remove from old parent
|
|
185
|
+
if item.parent_id is not None:
|
|
186
|
+
db.remove_child_from_parent(item.id, item.parent_id)
|
|
227
187
|
|
|
228
|
-
|
|
188
|
+
# add to new parent
|
|
189
|
+
if _is_int(parent_identifier):
|
|
190
|
+
new_parent_id = parent_identifier
|
|
191
|
+
elif not parent_identifier: # anything evaluating to false
|
|
192
|
+
new_parent_id=None
|
|
193
|
+
else:
|
|
194
|
+
new_parent_id = Controller._find_model_by_stringmatch("name", parent_identifier).id
|
|
195
|
+
|
|
196
|
+
db.update_item(item.id, parent_id=new_parent_id)
|
|
197
|
+
if new_parent_id is not None:
|
|
198
|
+
db.add_child_to_parent(item.id, new_parent_id)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
db.commit()
|
|
229
202
|
|
|
230
203
|
def add_dependency(src_id: int|str, dst_id: int|str):
|
|
231
204
|
"""Adds a depedency src -> dst, meaning src depends on dst"""
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
db.
|
|
205
|
+
dependency_ids = db.get_item_attr(src_id, "dependency_ids")
|
|
206
|
+
dependency_ids.append(dst_id)
|
|
207
|
+
db.update_item(src_id, dependency_ids=dependency_ids)
|
|
235
208
|
db.commit()
|
|
236
209
|
|
|
237
210
|
# utilities
|
|
@@ -289,33 +262,21 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
289
262
|
case "show":
|
|
290
263
|
match args[1]:
|
|
291
264
|
case "all": Controller.show_all(*args[2:], **kwargs)
|
|
292
|
-
case "list": Controller.show_list(*args[2:], **kwargs)
|
|
293
|
-
case "lists": Controller.show_lists()
|
|
294
|
-
case "item": Controller.show_item(*args[2:], **kwargs)
|
|
295
|
-
case "items": Controller.show_item(*args[2:], **kwargs)
|
|
296
265
|
case "examples": Controller.show_examples()
|
|
297
|
-
case _ if _is_int(args[1]): Controller.
|
|
298
|
-
case _: Controller.
|
|
266
|
+
case _ if _is_int(args[1]): Controller.show_item(*args[1:], **kwargs)
|
|
267
|
+
case _: Controller.show_by_item_name(args[1], **kwargs)
|
|
299
268
|
|
|
300
269
|
case "create":
|
|
301
|
-
|
|
302
|
-
case "item": Controller.create_item(*args[2:], **kwargs)
|
|
303
|
-
case "list": Controller.create_list(*args[2:], **kwargs)
|
|
304
|
-
case "comment": Controller.create_comment(*args[2:], **kwargs)
|
|
305
|
-
case _: Controller.throw_error("uncrecognized create command", *args, **kwargs)
|
|
270
|
+
Controller.create_item(args[1], **kwargs)
|
|
306
271
|
|
|
307
272
|
case "update":
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
case _: Controller.throw_error("uncregnozed update command", *args, **kwargs)
|
|
311
|
-
|
|
273
|
+
Controller.update_item(args[1], **kwargs)
|
|
274
|
+
|
|
312
275
|
case "delete" | "remove":
|
|
313
276
|
match args[1]:
|
|
314
|
-
case _ if _is_int(args[1]): Controller.
|
|
315
|
-
case "item": Controller.delete(args[2])
|
|
316
|
-
case "list": Controller.delete(args[2])
|
|
277
|
+
case _ if _is_int(args[1]): Controller.delete_item(args[1])
|
|
317
278
|
case "completed" | "done": Controller.delete_completed()
|
|
318
|
-
case _: Controller.
|
|
279
|
+
case _: Controller.delete_item_by_name(args[1])
|
|
319
280
|
|
|
320
281
|
case "comment":
|
|
321
282
|
match args[1]:
|
|
@@ -338,7 +299,9 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
338
299
|
db.remove()
|
|
339
300
|
|
|
340
301
|
case "add":
|
|
341
|
-
|
|
302
|
+
parent_identifier = args[1]
|
|
303
|
+
item_name = args[2]
|
|
304
|
+
Controller.create_item(item_name, parent_identifier, **kwargs)
|
|
342
305
|
|
|
343
306
|
case "complete" | "done":
|
|
344
307
|
match args[1]:
|
|
@@ -355,10 +318,8 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
355
318
|
Controller.delete_completed()
|
|
356
319
|
|
|
357
320
|
case "move":
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
case _: Controller.throw_error("Unrecognized arguments", *args, **kwargs)
|
|
361
|
-
|
|
321
|
+
Controller.move_item(args[1], args[2])
|
|
322
|
+
|
|
362
323
|
case "depend":
|
|
363
324
|
src_id, dst_id = args[1:3]
|
|
364
325
|
match (src_id, dst_id):
|
|
@@ -377,6 +338,7 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
377
338
|
|
|
378
339
|
|
|
379
340
|
except Exception as e:
|
|
341
|
+
raise e
|
|
380
342
|
Controller.throw_error(f"encountered exception '{e}'", *args, **kwargs)
|
|
381
343
|
|
|
382
344
|
return 1
|
|
@@ -5,12 +5,12 @@ help_general = """
|
|
|
5
5
|
|
|
6
6
|
Welcome to Task! Here's what you can do:
|
|
7
7
|
|
|
8
|
-
'task show all' --> show all of your lists and item
|
|
8
|
+
'task show all' --> show all of your lists and item names, with their respective IDs prepended
|
|
9
9
|
'task show {id or substring}' --> find the list or item matching your identifier and show it using its respective type's show command
|
|
10
|
-
'task show list {id or substring}' --> show the list and all of its item
|
|
10
|
+
'task show list {id or substring}' --> show the list and all of its item names
|
|
11
11
|
'task show item {id}' --> show the associated item and all of its specified information
|
|
12
12
|
'task show items {id1},{id2},...,{idx}' --> show the associated items and all of their specified information
|
|
13
|
-
'task create item {list id or substring} {
|
|
13
|
+
'task create item {list id or substring} {name} {**kwargs}' --> Create a new item for the associated list. Can specify kwargs as --optional cli arguments.
|
|
14
14
|
'task create list {name}' --> create a new list by that name
|
|
15
15
|
'task delete {id}' --> deletes the list or item associated with that id
|
|
16
16
|
'task delete item {id}' --> deletes the item associated with that id
|