taskai-cli 0.1.6__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.6 → taskai_cli-1.0.0}/DEVLOG.md +84 -1
- {taskai_cli-0.1.6 → taskai_cli-1.0.0}/PKG-INFO +4 -4
- {taskai_cli-0.1.6 → 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.6 → taskai_cli-1.0.0}/pyproject.toml +1 -1
- {taskai_cli-0.1.6 → taskai_cli-1.0.0}/taskai/cli.py +100 -139
- {taskai_cli-0.1.6 → 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.6 → 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.6/taskai/json_dir_database.py +0 -204
- taskai_cli-0.1.6/taskai/models.py +0 -63
- taskai_cli-0.1.6/taskai/services/repair_database.py +0 -39
- taskai_cli-0.1.6/taskai/views.py +0 -94
- taskai_cli-0.1.6/test/test_json_dir_database.py +0 -80
- {taskai_cli-0.1.6 → taskai_cli-1.0.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {taskai_cli-0.1.6 → taskai_cli-1.0.0}/.gitignore +0 -0
- {taskai_cli-0.1.6 → taskai_cli-1.0.0}/docs/index.md +0 -0
- {taskai_cli-0.1.6 → taskai_cli-1.0.0}/mkdocs.yml +0 -0
- {taskai_cli-0.1.6 → taskai_cli-1.0.0}/taskai/config.py +0 -0
- {taskai_cli-0.1.6 → 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
|
+
|
|
@@ -11,7 +11,7 @@ import getpass
|
|
|
11
11
|
# local
|
|
12
12
|
from taskai.json_dir_database import JsonDirectoryDatabase
|
|
13
13
|
from taskai.views import view_lists, view_item, view_items
|
|
14
|
-
from taskai.models import
|
|
14
|
+
from taskai.models import TodoItem, Comment
|
|
15
15
|
from taskai.services.ai import ai_headstart_service, ai_natural_language_service
|
|
16
16
|
from taskai.services.user_setup import user_setup_service
|
|
17
17
|
from taskai.services.repair_database import repair_database_service
|
|
@@ -29,36 +29,43 @@ DB_PATH = ".taskai/task_db"
|
|
|
29
29
|
USER = os.getenv("USER") if sys.platform == "linux" else os.getenv('USERNAME')
|
|
30
30
|
db = JsonDirectoryDatabase(
|
|
31
31
|
DB_PATH,
|
|
32
|
-
USER
|
|
32
|
+
USER,
|
|
33
33
|
)
|
|
34
34
|
db.connect()
|
|
35
|
-
GlobalConfig.load_dict(db.
|
|
36
|
-
|
|
37
|
-
og_help = builtins.help
|
|
38
|
-
def new_help(*args, **kwargs):
|
|
39
|
-
print(f"help: args: {args}, kwargs: {kwargs}")
|
|
40
|
-
return og_help(*args, **kwargs)
|
|
41
|
-
builtins.help = new_help
|
|
35
|
+
GlobalConfig.load_dict(db.get_config())
|
|
42
36
|
|
|
43
37
|
class Controller:
|
|
44
38
|
|
|
45
39
|
# utilities
|
|
46
|
-
def _find_model_by_stringmatch(attr: str, pattern: str) -> TodoItem|
|
|
40
|
+
def _find_model_by_stringmatch(attr: str, pattern: str) -> TodoItem|Comment|None:
|
|
47
41
|
|
|
48
42
|
for record_type in [
|
|
49
|
-
TodoList,
|
|
50
43
|
TodoItem,
|
|
51
44
|
Comment
|
|
52
45
|
]:
|
|
53
|
-
batch_attrs = db.
|
|
46
|
+
batch_attrs = db.get_item_batch_attr(attr)
|
|
54
47
|
inside_out = {v: k for k, v in batch_attrs.items()} # TODO this is hacky
|
|
55
48
|
results = fnmatch.filter(batch_attrs.values(), pattern)
|
|
56
49
|
if results:
|
|
57
50
|
id_ = inside_out[results[0]] # might be duplication
|
|
58
|
-
return db.
|
|
59
|
-
|
|
51
|
+
return db.get_item(id_)
|
|
60
52
|
return None
|
|
61
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
|
+
]
|
|
62
69
|
|
|
63
70
|
def _debug(args, kwargs):
|
|
64
71
|
print("args:", args)
|
|
@@ -66,113 +73,69 @@ class Controller:
|
|
|
66
73
|
|
|
67
74
|
# CRUD
|
|
68
75
|
def show_all(show_done=True):
|
|
69
|
-
view_lists(db,
|
|
70
|
-
|
|
71
|
-
def
|
|
72
|
-
if id_ in db.items:
|
|
73
|
-
Controller.show_item(id_)
|
|
74
|
-
elif id_ in db.lists:
|
|
75
|
-
Controller.show_list(id_, show_done=show_done)
|
|
76
|
-
|
|
77
|
-
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):
|
|
78
79
|
model = Controller._find_model_by_stringmatch("name", value)
|
|
79
|
-
if
|
|
80
|
-
Controller.
|
|
80
|
+
if model:
|
|
81
|
+
Controller.show_item(model.id, show_done=show_done)
|
|
81
82
|
else:
|
|
82
|
-
print(f"Could not find
|
|
83
|
-
|
|
84
|
-
def show_list(list_id: int|str, show_done=True):
|
|
85
|
-
view_lists(db, [list_id], show_done=show_done)
|
|
86
|
-
|
|
87
|
-
def show_lists():
|
|
88
|
-
view_lists(db, db.lists.keys(), show_items=False)
|
|
83
|
+
print(f"Could not find item matching pattern '{value}'")
|
|
89
84
|
|
|
90
|
-
def show_item(item_id: int
|
|
91
|
-
view_item(db, item_id)
|
|
85
|
+
def show_item(item_id: int, **kwargs):
|
|
86
|
+
view_item(db, item_id, **kwargs)
|
|
92
87
|
|
|
93
|
-
def show_items(item_ids: str):
|
|
88
|
+
def show_items(item_ids: str, **kwargs):
|
|
94
89
|
item_ids = item_ids.split(",")
|
|
95
|
-
view_items(db, item_ids)
|
|
90
|
+
view_items(db, item_ids, **kwargs)
|
|
96
91
|
|
|
97
92
|
def show_examples():
|
|
98
93
|
...
|
|
99
94
|
print("Not implemented yet")
|
|
100
95
|
|
|
101
|
-
def
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# TODO this should be just lists, not models
|
|
113
|
-
list_id = list_.id
|
|
114
|
-
|
|
115
|
-
item = TodoItem(title=title, list_id=list_id)
|
|
116
|
-
|
|
117
|
-
for k, v in kwargs.items():
|
|
118
|
-
if v is None:
|
|
119
|
-
continue
|
|
120
|
-
match k:
|
|
121
|
-
case "completed": item.completed = bool(v)
|
|
122
|
-
case "description": item.description = str(v)
|
|
123
|
-
case "due_by": item.due_by = datetime.strptime(v, "%m-%d-%Y")
|
|
124
|
-
case "parent": item.parent = str(v)
|
|
125
|
-
case "priority": item.priority = int(v)
|
|
126
|
-
case "depends_on": item.dependency_ids.extend(v.split(","))
|
|
127
|
-
# TODO handle recurrence
|
|
128
|
-
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}'")
|
|
129
107
|
db.commit()
|
|
130
108
|
|
|
131
109
|
def create_comment(item_id: int|str, content: str):
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
content=content
|
|
135
|
-
)
|
|
136
|
-
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}'")
|
|
137
112
|
db.commit()
|
|
138
113
|
|
|
139
114
|
def update_item(item_id: int|str, **kwargs):
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
match k:
|
|
145
|
-
case "title": item.title = str(v)
|
|
146
|
-
case "list_id": item.list_id = str(v)
|
|
147
|
-
case "completed": item.completed = bool(v)
|
|
148
|
-
case "description": item.description = str(v)
|
|
149
|
-
case "due_by": item.due_by = datetime.strptime(v, "%m-%d-%Y")
|
|
150
|
-
case "parent": item.parent = str(v)
|
|
151
|
-
case "priority": item.priority = int(v)
|
|
152
|
-
# TODO handle recurrence
|
|
153
|
-
|
|
154
|
-
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}")
|
|
155
119
|
db.commit()
|
|
156
120
|
|
|
157
|
-
def
|
|
158
|
-
db.
|
|
121
|
+
def delete_item(id_: int|str):
|
|
122
|
+
db.delete_item(id_)
|
|
159
123
|
db.commit()
|
|
160
|
-
print(f"Deleted {id_}")
|
|
124
|
+
print(f"Deleted item {id_}")
|
|
161
125
|
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
if
|
|
165
|
-
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)
|
|
166
130
|
db.commit()
|
|
167
131
|
else:
|
|
168
132
|
Controller.throw_error("Cannot find list by name")
|
|
169
|
-
|
|
170
133
|
|
|
171
134
|
def delete_completed():
|
|
172
|
-
for item_id in db.
|
|
173
|
-
item: TodoItem = db.
|
|
135
|
+
for item_id in db.get_item_ids():
|
|
136
|
+
item: TodoItem = db.get_item(item_id)
|
|
174
137
|
if item.completed:
|
|
175
|
-
db.
|
|
138
|
+
db.delete_item(item_id)
|
|
176
139
|
db.commit()
|
|
177
140
|
|
|
178
141
|
def ai_headstart(item_id: int|str):
|
|
@@ -186,21 +149,23 @@ class Controller:
|
|
|
186
149
|
|
|
187
150
|
def throw_error(error_description: str, *args, **kwargs):
|
|
188
151
|
print(f"[red]ERROR: {error_description}[/red]\nargs={args}\nkwargs={kwargs}")
|
|
152
|
+
import sys
|
|
153
|
+
sys.exit(-1)
|
|
189
154
|
|
|
190
155
|
def get_config_value(key: str):
|
|
191
|
-
print(db.
|
|
156
|
+
print(getattr(db.get_config(), key))
|
|
192
157
|
|
|
193
158
|
def list_config():
|
|
194
159
|
for k, v in db.get_config().model_dump().items():
|
|
195
160
|
print(f"{k}={v}")
|
|
196
161
|
|
|
197
162
|
def set_config_value(key: str, value: any):
|
|
198
|
-
db.
|
|
163
|
+
db.update_config(**{key: value})
|
|
199
164
|
db.commit()
|
|
200
165
|
print(f"setting {key}={value}")
|
|
201
166
|
|
|
202
167
|
def remove_config_value(key: str):
|
|
203
|
-
db.
|
|
168
|
+
db.update_config(**{key: None})
|
|
204
169
|
db.commit()
|
|
205
170
|
|
|
206
171
|
def run_setup_service():
|
|
@@ -209,30 +174,37 @@ class Controller:
|
|
|
209
174
|
def repair_service():
|
|
210
175
|
repair_database_service(db)
|
|
211
176
|
|
|
212
|
-
def move_item(item_id: int|str,
|
|
213
|
-
|
|
214
|
-
if item_id not in db.items:
|
|
215
|
-
Controller.throw_error(f"Couldn't find items with id {item_id}")
|
|
216
|
-
item: TodoItem = db.read(item_id)
|
|
177
|
+
def move_item(item_id: int|str, parent_identifier: int|str):
|
|
217
178
|
|
|
218
|
-
if _is_int(
|
|
219
|
-
|
|
179
|
+
if _is_int(item_id):
|
|
180
|
+
item = db.get_item(item_id)
|
|
220
181
|
else:
|
|
221
|
-
|
|
222
|
-
if not new_list_:
|
|
223
|
-
Controller.throw_error(f"Couldn't locate list by identifier {list_identifier}")
|
|
182
|
+
item = Controller._find_model_by_stringmatch("name", item_id)
|
|
224
183
|
|
|
225
|
-
#
|
|
226
|
-
item.
|
|
227
|
-
|
|
184
|
+
# remove from old parent
|
|
185
|
+
if item.parent_id is not None:
|
|
186
|
+
db.remove_child_from_parent(item.id, item.parent_id)
|
|
228
187
|
|
|
229
|
-
|
|
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()
|
|
230
202
|
|
|
231
203
|
def add_dependency(src_id: int|str, dst_id: int|str):
|
|
232
204
|
"""Adds a depedency src -> dst, meaning src depends on dst"""
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
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)
|
|
236
208
|
db.commit()
|
|
237
209
|
|
|
238
210
|
# utilities
|
|
@@ -290,33 +262,21 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
290
262
|
case "show":
|
|
291
263
|
match args[1]:
|
|
292
264
|
case "all": Controller.show_all(*args[2:], **kwargs)
|
|
293
|
-
case "list": Controller.show_list(*args[2:], **kwargs)
|
|
294
|
-
case "lists": Controller.show_lists()
|
|
295
|
-
case "item": Controller.show_item(*args[2:], **kwargs)
|
|
296
|
-
case "items": Controller.show_item(*args[2:], **kwargs)
|
|
297
265
|
case "examples": Controller.show_examples()
|
|
298
|
-
case _ if _is_int(args[1]): Controller.
|
|
299
|
-
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)
|
|
300
268
|
|
|
301
269
|
case "create":
|
|
302
|
-
|
|
303
|
-
case "item": Controller.create_item(*args[2:], **kwargs)
|
|
304
|
-
case "list": Controller.create_list(*args[2:], **kwargs)
|
|
305
|
-
case "comment": Controller.create_comment(*args[2:], **kwargs)
|
|
306
|
-
case _: Controller.throw_error("uncrecognized create command", *args, **kwargs)
|
|
270
|
+
Controller.create_item(args[1], **kwargs)
|
|
307
271
|
|
|
308
272
|
case "update":
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
case _: Controller.throw_error("uncregnozed update command", *args, **kwargs)
|
|
312
|
-
|
|
273
|
+
Controller.update_item(args[1], **kwargs)
|
|
274
|
+
|
|
313
275
|
case "delete" | "remove":
|
|
314
276
|
match args[1]:
|
|
315
|
-
case _ if _is_int(args[1]): Controller.
|
|
316
|
-
case "item": Controller.delete(args[2])
|
|
317
|
-
case "list": Controller.delete(args[2])
|
|
277
|
+
case _ if _is_int(args[1]): Controller.delete_item(args[1])
|
|
318
278
|
case "completed" | "done": Controller.delete_completed()
|
|
319
|
-
case _: Controller.
|
|
279
|
+
case _: Controller.delete_item_by_name(args[1])
|
|
320
280
|
|
|
321
281
|
case "comment":
|
|
322
282
|
match args[1]:
|
|
@@ -339,7 +299,9 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
339
299
|
db.remove()
|
|
340
300
|
|
|
341
301
|
case "add":
|
|
342
|
-
|
|
302
|
+
parent_identifier = args[1]
|
|
303
|
+
item_name = args[2]
|
|
304
|
+
Controller.create_item(item_name, parent_identifier, **kwargs)
|
|
343
305
|
|
|
344
306
|
case "complete" | "done":
|
|
345
307
|
match args[1]:
|
|
@@ -356,10 +318,8 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
356
318
|
Controller.delete_completed()
|
|
357
319
|
|
|
358
320
|
case "move":
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
case _: Controller.throw_error("Unrecognized arguments", *args, **kwargs)
|
|
362
|
-
|
|
321
|
+
Controller.move_item(args[1], args[2])
|
|
322
|
+
|
|
363
323
|
case "depend":
|
|
364
324
|
src_id, dst_id = args[1:3]
|
|
365
325
|
match (src_id, dst_id):
|
|
@@ -378,6 +338,7 @@ def execute_commands(*args, **kwargs) -> int:
|
|
|
378
338
|
|
|
379
339
|
|
|
380
340
|
except Exception as e:
|
|
341
|
+
raise e
|
|
381
342
|
Controller.throw_error(f"encountered exception '{e}'", *args, **kwargs)
|
|
382
343
|
|
|
383
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
|