task-manager-cli-vedaanth 0.1.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.
- task_manager_cli_vedaanth-0.1.0/PKG-INFO +10 -0
- task_manager_cli_vedaanth-0.1.0/pyproject.toml +22 -0
- task_manager_cli_vedaanth-0.1.0/setup.cfg +4 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager/__init__.py +0 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager/__main__.py +3 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager/api_client.py +123 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager/auth.py +23 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager/cli.py +456 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager_cli_vedaanth.egg-info/PKG-INFO +10 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager_cli_vedaanth.egg-info/SOURCES.txt +12 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager_cli_vedaanth.egg-info/dependency_links.txt +1 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager_cli_vedaanth.egg-info/entry_points.txt +2 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager_cli_vedaanth.egg-info/requires.txt +5 -0
- task_manager_cli_vedaanth-0.1.0/src/task_manager_cli_vedaanth.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: task-manager-cli-vedaanth
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Interactive terminal client for Task Manager
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: typer
|
|
7
|
+
Requires-Dist: rich
|
|
8
|
+
Requires-Dist: httpx
|
|
9
|
+
Requires-Dist: pydantic
|
|
10
|
+
Requires-Dist: platformdirs
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "task-manager-cli-vedaanth"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Interactive terminal client for Task Manager"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"typer",
|
|
12
|
+
"rich",
|
|
13
|
+
"httpx",
|
|
14
|
+
"pydantic",
|
|
15
|
+
"platformdirs"
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
task-manager = "task_manager.cli:app"
|
|
20
|
+
|
|
21
|
+
[tool.setuptools.packages.find]
|
|
22
|
+
where = ["src"]
|
|
File without changes
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
BASE_URL = "https://task-manager-cli-eu90.onrender.com"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def register_user(username, email, password):
|
|
8
|
+
response = httpx.post(
|
|
9
|
+
f"{BASE_URL}/api/register/",
|
|
10
|
+
json={
|
|
11
|
+
"username": username,
|
|
12
|
+
"email": email,
|
|
13
|
+
"password": password,
|
|
14
|
+
},
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
return response
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
This file's job is:
|
|
22
|
+
CLI
|
|
23
|
+
↓
|
|
24
|
+
api_client.py
|
|
25
|
+
↓
|
|
26
|
+
HTTP request
|
|
27
|
+
↓
|
|
28
|
+
Django API"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def login_user(username, password):
|
|
32
|
+
response = httpx.post(
|
|
33
|
+
f"{BASE_URL}/api/token/",
|
|
34
|
+
json={
|
|
35
|
+
"username": username,
|
|
36
|
+
"password": password,
|
|
37
|
+
},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return response
|
|
41
|
+
|
|
42
|
+
def get_tasks(access_token):
|
|
43
|
+
response = httpx.get(
|
|
44
|
+
f"{BASE_URL}/api/tasks/",
|
|
45
|
+
headers={
|
|
46
|
+
"Authorization": f"Bearer {access_token}"
|
|
47
|
+
#That's how your CLI tells Django:
|
|
48
|
+
#"This request belongs to the user represented by this JWT."""
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return response
|
|
53
|
+
def create_task(access_token, title, description=""):
|
|
54
|
+
response = httpx.post(
|
|
55
|
+
f"{BASE_URL}/api/tasks/",
|
|
56
|
+
headers={
|
|
57
|
+
"Authorization": f"Bearer {access_token}"
|
|
58
|
+
},
|
|
59
|
+
json={
|
|
60
|
+
"title": title,
|
|
61
|
+
"description": description,
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return response
|
|
66
|
+
"""The flow is:
|
|
67
|
+
CLI
|
|
68
|
+
↓
|
|
69
|
+
create_task()
|
|
70
|
+
↓
|
|
71
|
+
POST /api/tasks/
|
|
72
|
+
↓
|
|
73
|
+
JWT → Django identifies request.user
|
|
74
|
+
↓
|
|
75
|
+
serializer.save(owner=request.user)
|
|
76
|
+
↓
|
|
77
|
+
Database"""
|
|
78
|
+
def complete_task(access_token, task_id):
|
|
79
|
+
response = httpx.patch(
|
|
80
|
+
f"{BASE_URL}/api/task/{task_id}/",
|
|
81
|
+
headers={
|
|
82
|
+
"Authorization": f"Bearer {access_token}"
|
|
83
|
+
},
|
|
84
|
+
json={
|
|
85
|
+
"done": True
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return response
|
|
90
|
+
|
|
91
|
+
def update_task(access_token, task_id, title, description):
|
|
92
|
+
response = httpx.patch(
|
|
93
|
+
f"{BASE_URL}/api/task/{task_id}/",
|
|
94
|
+
headers={
|
|
95
|
+
"Authorization": f"Bearer {access_token}"
|
|
96
|
+
},
|
|
97
|
+
json={
|
|
98
|
+
"title": title,
|
|
99
|
+
"description": description
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return response
|
|
104
|
+
def get_task(access_token, task_id):
|
|
105
|
+
response = httpx.get(
|
|
106
|
+
f"{BASE_URL}/api/task/{task_id}/",
|
|
107
|
+
headers={
|
|
108
|
+
"Authorization": f"Bearer {access_token}"
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return response
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def delete_task(access_token, task_id):
|
|
116
|
+
response = httpx.delete(
|
|
117
|
+
f"{BASE_URL}/api/task/{task_id}/",
|
|
118
|
+
headers={
|
|
119
|
+
"Authorization": f"Bearer {access_token}"
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return response
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
TOKEN_FILE = Path.home() / ".task_manager_tokens.json"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def save_tokens(tokens):
|
|
9
|
+
with open(TOKEN_FILE, "w") as file:
|
|
10
|
+
json.dump(tokens, file)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_tokens():
|
|
14
|
+
if not TOKEN_FILE.exists():
|
|
15
|
+
return None
|
|
16
|
+
|
|
17
|
+
with open(TOKEN_FILE, "r") as file:
|
|
18
|
+
return json.load(file)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def logout():
|
|
22
|
+
if TOKEN_FILE.exists():
|
|
23
|
+
TOKEN_FILE.unlink()
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
from urllib import response
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
from .api_client import (
|
|
7
|
+
get_tasks,
|
|
8
|
+
get_task,
|
|
9
|
+
create_task,
|
|
10
|
+
complete_task,
|
|
11
|
+
update_task,
|
|
12
|
+
delete_task,
|
|
13
|
+
login_user,
|
|
14
|
+
register_user,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from .auth import (
|
|
18
|
+
save_tokens,
|
|
19
|
+
load_tokens,
|
|
20
|
+
logout,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
app = typer.Typer()
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@app.callback(invoke_without_command=True)
|
|
28
|
+
def main():
|
|
29
|
+
|
|
30
|
+
console.print(
|
|
31
|
+
"\n[bold cyan]╭──────────────────────────────────────╮[/bold cyan]"
|
|
32
|
+
)
|
|
33
|
+
console.print(
|
|
34
|
+
"[bold cyan]│ TASK MANAGER CLI │[/bold cyan]"
|
|
35
|
+
)
|
|
36
|
+
console.print(
|
|
37
|
+
"[bold cyan]╰──────────────────────────────────────╯[/bold cyan]"
|
|
38
|
+
)
|
|
39
|
+
console.print("\nWelcome to Task Manager CLI!")
|
|
40
|
+
console.print("Type [bold]help[/bold] to see available commands.")
|
|
41
|
+
console.print("Type [bold]exit[/bold] to quit.\n")
|
|
42
|
+
|
|
43
|
+
while True:
|
|
44
|
+
|
|
45
|
+
command = typer.prompt("task")
|
|
46
|
+
|
|
47
|
+
# Exit the CLI
|
|
48
|
+
if command.lower() == "exit":
|
|
49
|
+
|
|
50
|
+
console.print("[yellow]Goodbye![/yellow]")
|
|
51
|
+
break
|
|
52
|
+
|
|
53
|
+
# Show available CLI commands
|
|
54
|
+
elif command.lower() == "help":
|
|
55
|
+
|
|
56
|
+
console.print("\n[bold cyan]AUTH[/bold cyan]")
|
|
57
|
+
console.print(" register - Create a new account")
|
|
58
|
+
console.print(" login - Login to your account")
|
|
59
|
+
console.print(" logout - Logout from your account")
|
|
60
|
+
console.print(" me - Show current user")
|
|
61
|
+
|
|
62
|
+
console.print("\n[bold cyan]TASKS[/bold cyan]")
|
|
63
|
+
console.print(" list - Show your tasks")
|
|
64
|
+
console.print(" get - Get a task by ID")
|
|
65
|
+
console.print(" create - Create a new task")
|
|
66
|
+
console.print(" update - Update a task")
|
|
67
|
+
console.print(" delete - Delete a task")
|
|
68
|
+
console.print(" done - Mark a task as completed")
|
|
69
|
+
|
|
70
|
+
console.print("\n[bold cyan]SYSTEM[/bold cyan]")
|
|
71
|
+
console.print(" help - Show this help")
|
|
72
|
+
console.print(" clear - Clear the terminal")
|
|
73
|
+
console.print(" exit - Exit the CLI\n")
|
|
74
|
+
|
|
75
|
+
# Clear the terminal
|
|
76
|
+
elif command.lower() == "clear":
|
|
77
|
+
|
|
78
|
+
console.clear()
|
|
79
|
+
|
|
80
|
+
# Register a new user account
|
|
81
|
+
elif command.lower() == "register":
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
Flow:
|
|
85
|
+
task: register
|
|
86
|
+
↓
|
|
87
|
+
Ask username
|
|
88
|
+
↓
|
|
89
|
+
Ask email
|
|
90
|
+
↓
|
|
91
|
+
Ask password
|
|
92
|
+
↓
|
|
93
|
+
api_client.py
|
|
94
|
+
↓
|
|
95
|
+
POST /api/register/
|
|
96
|
+
↓
|
|
97
|
+
Django creates the user
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
console.print("\n[bold cyan]Create Account[/bold cyan]")
|
|
101
|
+
|
|
102
|
+
username = typer.prompt("Username")
|
|
103
|
+
email = typer.prompt("Email")
|
|
104
|
+
password = typer.prompt(
|
|
105
|
+
"Password",
|
|
106
|
+
hide_input=True
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
response = register_user(
|
|
110
|
+
username,
|
|
111
|
+
email,
|
|
112
|
+
password
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if response.status_code == 201:
|
|
116
|
+
|
|
117
|
+
console.print(
|
|
118
|
+
"[green]✓ Account created successfully![/green]"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
else:
|
|
122
|
+
|
|
123
|
+
console.print(
|
|
124
|
+
f"[red]✗ Registration failed:[/red] "
|
|
125
|
+
f"{response.text}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Login and save JWT tokens locally
|
|
129
|
+
elif command.lower() == "login":
|
|
130
|
+
|
|
131
|
+
console.print("\n[bold cyan]Login[/bold cyan]")
|
|
132
|
+
|
|
133
|
+
username = typer.prompt("Username")
|
|
134
|
+
password = typer.prompt(
|
|
135
|
+
"Password",
|
|
136
|
+
hide_input=True
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
response = login_user(
|
|
140
|
+
username,
|
|
141
|
+
password
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if response.status_code == 200:
|
|
145
|
+
|
|
146
|
+
tokens = response.json()
|
|
147
|
+
|
|
148
|
+
# Save access and refresh tokens locally
|
|
149
|
+
save_tokens(tokens)
|
|
150
|
+
|
|
151
|
+
console.print(
|
|
152
|
+
"[green]✓ Login successful![/green]"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
else:
|
|
156
|
+
|
|
157
|
+
console.print(
|
|
158
|
+
f"[red]✗ Login failed:[/red] "
|
|
159
|
+
f"{response.text}"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Retrieve and display the user's tasks
|
|
163
|
+
elif command.lower() == "list":
|
|
164
|
+
|
|
165
|
+
# Load the saved JWT tokens
|
|
166
|
+
tokens = load_tokens()
|
|
167
|
+
|
|
168
|
+
if not tokens:
|
|
169
|
+
|
|
170
|
+
console.print(
|
|
171
|
+
"[red]Please login first.[/red]"
|
|
172
|
+
)
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
# Send the access token with the API request
|
|
176
|
+
response = get_tasks(
|
|
177
|
+
tokens["access"]
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if response.status_code == 200:
|
|
181
|
+
|
|
182
|
+
tasks = response.json()
|
|
183
|
+
|
|
184
|
+
# Check whether the user has any tasks
|
|
185
|
+
if not tasks:
|
|
186
|
+
|
|
187
|
+
console.print(
|
|
188
|
+
"\n[yellow]You don't have any tasks yet.[/yellow]"
|
|
189
|
+
)
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
console.print(
|
|
193
|
+
"\n[bold cyan]Your Tasks[/bold cyan]"
|
|
194
|
+
)
|
|
195
|
+
console.print(
|
|
196
|
+
"────────────────────────────────────────"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Display every task
|
|
200
|
+
for task in tasks:
|
|
201
|
+
|
|
202
|
+
# Show ✓ for completed tasks
|
|
203
|
+
# Show ☐ for incomplete tasks
|
|
204
|
+
if task["done"]:
|
|
205
|
+
status_icon = "[green]✓[/green]"
|
|
206
|
+
else:
|
|
207
|
+
status_icon = "[yellow]☐[/yellow]"
|
|
208
|
+
|
|
209
|
+
console.print(
|
|
210
|
+
f"{status_icon} "
|
|
211
|
+
f"[bold]ID: {task['id']}[/bold]"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Display the task title
|
|
215
|
+
console.print(
|
|
216
|
+
f" [bold]{task['title']}[/bold]"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Display the task description
|
|
220
|
+
description = task.get(
|
|
221
|
+
"description",
|
|
222
|
+
""
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Break long descriptions into readable lines
|
|
226
|
+
words = description.split()
|
|
227
|
+
lines = []
|
|
228
|
+
current_line = ""
|
|
229
|
+
|
|
230
|
+
for word in words:
|
|
231
|
+
|
|
232
|
+
if len(current_line) + len(word) + 1 <= 60:
|
|
233
|
+
|
|
234
|
+
current_line += word + " "
|
|
235
|
+
|
|
236
|
+
else:
|
|
237
|
+
|
|
238
|
+
lines.append(
|
|
239
|
+
current_line.strip()
|
|
240
|
+
)
|
|
241
|
+
current_line = word + " "
|
|
242
|
+
|
|
243
|
+
if current_line:
|
|
244
|
+
|
|
245
|
+
lines.append(
|
|
246
|
+
current_line.strip()
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
for line in lines:
|
|
250
|
+
|
|
251
|
+
console.print(
|
|
252
|
+
f" {line}"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
console.print()
|
|
256
|
+
|
|
257
|
+
console.print(
|
|
258
|
+
"────────────────────────────────────────"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
else:
|
|
262
|
+
|
|
263
|
+
console.print(
|
|
264
|
+
f"[red]Failed to get tasks:[/red] "
|
|
265
|
+
f"{response.text}"
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Create a new task
|
|
269
|
+
elif command.lower() == "create":
|
|
270
|
+
|
|
271
|
+
# Load the saved JWT tokens
|
|
272
|
+
tokens = load_tokens()
|
|
273
|
+
|
|
274
|
+
if not tokens:
|
|
275
|
+
|
|
276
|
+
console.print(
|
|
277
|
+
"[red]Please login first.[/red]"
|
|
278
|
+
)
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
console.print(
|
|
282
|
+
"\n[bold cyan]Create Task[/bold cyan]"
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
title = typer.prompt("Title")
|
|
286
|
+
|
|
287
|
+
description = typer.prompt(
|
|
288
|
+
"Description",
|
|
289
|
+
default=""
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Send the new task to the Django API
|
|
293
|
+
response = create_task(
|
|
294
|
+
tokens["access"],
|
|
295
|
+
title,
|
|
296
|
+
description
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
if response.status_code == 201:
|
|
300
|
+
|
|
301
|
+
console.print(
|
|
302
|
+
"[green]✓ Task created successfully![/green]"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
else:
|
|
306
|
+
|
|
307
|
+
console.print(
|
|
308
|
+
f"[red]✗ Failed to create task:[/red] "
|
|
309
|
+
f"{response.text}"
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Mark an existing task as completed
|
|
313
|
+
elif command.lower() == "done":
|
|
314
|
+
|
|
315
|
+
# Load the saved JWT tokens
|
|
316
|
+
tokens = load_tokens()
|
|
317
|
+
|
|
318
|
+
if not tokens:
|
|
319
|
+
|
|
320
|
+
console.print(
|
|
321
|
+
"[red]Please login first.[/red]"
|
|
322
|
+
)
|
|
323
|
+
continue
|
|
324
|
+
|
|
325
|
+
# Ask which task should be marked as completed
|
|
326
|
+
task_id = typer.prompt("Task ID")
|
|
327
|
+
|
|
328
|
+
# Send the completion request to the Django API
|
|
329
|
+
response = complete_task(
|
|
330
|
+
tokens["access"],
|
|
331
|
+
task_id
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
if response.status_code == 200:
|
|
335
|
+
|
|
336
|
+
console.print(
|
|
337
|
+
"[green]✓ Task marked as completed![/green]"
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
else:
|
|
341
|
+
|
|
342
|
+
console.print(
|
|
343
|
+
f"[red]✗ Failed to complete task:[/red] "
|
|
344
|
+
f"{response.text}"
|
|
345
|
+
)
|
|
346
|
+
elif command.lower() == "edit":
|
|
347
|
+
tokens = load_tokens()
|
|
348
|
+
|
|
349
|
+
if not tokens:
|
|
350
|
+
console.print("[red]Please login first.[/red]")
|
|
351
|
+
continue
|
|
352
|
+
|
|
353
|
+
console.print("\n[bold cyan]Edit Task[/bold cyan]")
|
|
354
|
+
|
|
355
|
+
task_id = typer.prompt("Task ID")
|
|
356
|
+
title = typer.prompt("New title")
|
|
357
|
+
description = typer.prompt("New description")
|
|
358
|
+
|
|
359
|
+
response = update_task(
|
|
360
|
+
tokens["access"],
|
|
361
|
+
task_id,
|
|
362
|
+
title,
|
|
363
|
+
description
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
if response.status_code == 200:
|
|
367
|
+
console.print("[green]✓ Task updated successfully![/green]")
|
|
368
|
+
else:
|
|
369
|
+
console.print(
|
|
370
|
+
f"[red]✗ Failed to update task:[/red] "
|
|
371
|
+
f"{response.text}"
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
# Handle commands that are not implemented or recognized
|
|
375
|
+
elif command.lower() == "get":
|
|
376
|
+
tokens = load_tokens()
|
|
377
|
+
|
|
378
|
+
if not tokens:
|
|
379
|
+
console.print("[red]Please login first.[/red]")
|
|
380
|
+
continue
|
|
381
|
+
|
|
382
|
+
task_id = typer.prompt("Task ID")
|
|
383
|
+
|
|
384
|
+
response = get_task(
|
|
385
|
+
tokens["access"],
|
|
386
|
+
task_id
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
if response.status_code == 200:
|
|
390
|
+
task = response.json()
|
|
391
|
+
|
|
392
|
+
status_icon = (
|
|
393
|
+
"[green]✓[/green]"
|
|
394
|
+
if task["done"]
|
|
395
|
+
else "[yellow]☐[/yellow]"
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
console.print(f"\n{status_icon} [bold]ID: {task['id']}[/bold]")
|
|
399
|
+
console.print(f" [bold]{task['title']}[/bold]")
|
|
400
|
+
console.print(f" {task.get('description', '')}")
|
|
401
|
+
else:
|
|
402
|
+
console.print(
|
|
403
|
+
f"[red]✗ Failed to get task:[/red] "
|
|
404
|
+
f"{response.text}"
|
|
405
|
+
)
|
|
406
|
+
elif command.lower() == "delete":
|
|
407
|
+
tokens = load_tokens()
|
|
408
|
+
|
|
409
|
+
if not tokens:
|
|
410
|
+
console.print("[red]Please login first.[/red]")
|
|
411
|
+
continue
|
|
412
|
+
|
|
413
|
+
task_id = typer.prompt("Task ID")
|
|
414
|
+
|
|
415
|
+
confirm = typer.confirm(
|
|
416
|
+
f"Are you sure you want to delete task {task_id}?"
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
if not confirm:
|
|
420
|
+
console.print("[yellow]Delete cancelled.[/yellow]")
|
|
421
|
+
continue
|
|
422
|
+
|
|
423
|
+
response = delete_task(
|
|
424
|
+
tokens["access"],
|
|
425
|
+
task_id
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
if response.status_code == 204:
|
|
429
|
+
console.print(
|
|
430
|
+
"[green]✓ Task deleted successfully![/green]"
|
|
431
|
+
)
|
|
432
|
+
else:
|
|
433
|
+
console.print(
|
|
434
|
+
f"[red]✗ Failed to delete task:[/red] "
|
|
435
|
+
f"{response.text}"
|
|
436
|
+
)
|
|
437
|
+
elif command.lower() == "logout":
|
|
438
|
+
tokens = load_tokens()
|
|
439
|
+
|
|
440
|
+
if not tokens:
|
|
441
|
+
console.print("[yellow]You are not logged in.[/yellow]")
|
|
442
|
+
continue
|
|
443
|
+
|
|
444
|
+
logout()
|
|
445
|
+
|
|
446
|
+
console.print("[green]✓ Logged out successfully![/green]")
|
|
447
|
+
|
|
448
|
+
else:
|
|
449
|
+
|
|
450
|
+
console.print(
|
|
451
|
+
f"[red]Unknown command:[/red] {command}"
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
if __name__ == "__main__":
|
|
456
|
+
app()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: task-manager-cli-vedaanth
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Interactive terminal client for Task Manager
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: typer
|
|
7
|
+
Requires-Dist: rich
|
|
8
|
+
Requires-Dist: httpx
|
|
9
|
+
Requires-Dist: pydantic
|
|
10
|
+
Requires-Dist: platformdirs
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
src/task_manager/__init__.py
|
|
3
|
+
src/task_manager/__main__.py
|
|
4
|
+
src/task_manager/api_client.py
|
|
5
|
+
src/task_manager/auth.py
|
|
6
|
+
src/task_manager/cli.py
|
|
7
|
+
src/task_manager_cli_vedaanth.egg-info/PKG-INFO
|
|
8
|
+
src/task_manager_cli_vedaanth.egg-info/SOURCES.txt
|
|
9
|
+
src/task_manager_cli_vedaanth.egg-info/dependency_links.txt
|
|
10
|
+
src/task_manager_cli_vedaanth.egg-info/entry_points.txt
|
|
11
|
+
src/task_manager_cli_vedaanth.egg-info/requires.txt
|
|
12
|
+
src/task_manager_cli_vedaanth.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
task_manager
|