todoist-cli-tool 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.
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: todoist-cli-tool
3
+ Version: 0.1.0
4
+ Summary: Todoist CLI - Manage your tasks from the command line
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: pydantic>=2.12.3
8
+ Requires-Dist: python-dotenv>=1.1.1
9
+ Requires-Dist: questionary>=2.1.1
10
+ Requires-Dist: requests>=2.32.4
11
+ Requires-Dist: rich>=14.0.0
12
+ Requires-Dist: textual>=3.3.0
13
+ Requires-Dist: typer>=0.20.0
14
+
15
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,18 @@
1
+ [project]
2
+ name = "todoist-cli-tool"
3
+ version = "0.1.0"
4
+ description = "Todoist CLI - Manage your tasks from the command line"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "pydantic>=2.12.3",
9
+ "python-dotenv>=1.1.1",
10
+ "questionary>=2.1.1",
11
+ "requests>=2.32.4",
12
+ "rich>=14.0.0",
13
+ "textual>=3.3.0",
14
+ "typer>=0.20.0",
15
+ ]
16
+
17
+ [project.scripts]
18
+ todoist = "main:app"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,27 @@
1
+ from typing import Any, Dict, Optional
2
+ class TodoistException(Exception):
3
+ def __init__(
4
+ self,
5
+ message: str,
6
+ code: Optional[str | int] = None,
7
+ details: Optional[Dict[str, Any]] = None
8
+ ) -> None:
9
+ # Call the base Exception's __init__ with the main message.
10
+ # This ensures `str(e)` works out of the box.
11
+ super().__init__(message)
12
+
13
+ self.message: str = message
14
+ self.code: Optional[str | int] = code
15
+ self.details: Dict[str, Any] = details or {}
16
+
17
+ def __str__(self) -> str:
18
+ """Custom string representation for logging."""
19
+ base_str: str = f"[{self.code}] {self.message}" if self.code else self.message
20
+
21
+ if self.details:
22
+ details_str: str = ", ".join(f"{k}={v!r}" for k, v in self.details.items())
23
+ return f"{base_str} (Details: {details_str})"
24
+
25
+ return base_str
26
+ def name(self) -> str:
27
+ return self.__class__.__name__
@@ -0,0 +1,46 @@
1
+ from pydantic import BaseModel, Field
2
+ from typing import Optional, Any
3
+ from datetime import date, datetime
4
+
5
+
6
+ class TaskDue(BaseModel):
7
+ date: str # Represents the date part, e.g., "2025-10-11"
8
+ string: str
9
+ lang: str
10
+ is_recurring: bool
11
+
12
+
13
+ class Task(BaseModel):
14
+ id : str | None = None
15
+ content: str
16
+ description: str
17
+ project_id: str | None = None
18
+ priority: int
19
+
20
+
21
+ class TaskUpdate(BaseModel):
22
+ content: Optional[str] = None
23
+ description: Optional[str] = None
24
+ priority: Optional[int] = None
25
+
26
+
27
+ class TodoistModel(BaseModel):
28
+ id: str
29
+ assigner_id: Optional[str] = Field(None, alias="assigned_by_uid")
30
+ assignee_id: Optional[str] = Field(None, alias="responsible_uid")
31
+ project_id: str
32
+ section_id: Optional[str] = None
33
+ parent_id: Optional[str] = None
34
+ order: int = Field(..., alias="child_order")
35
+ content: str
36
+ description: str
37
+ is_completed: bool = Field(..., alias="checked")
38
+ labels: list[str]
39
+ priority: int
40
+ comment_count: int = Field(..., alias="note_count")
41
+ creator_id: str = Field(..., alias="added_by_uid")
42
+ created_at: datetime = Field(..., alias="added_at")
43
+ due: Optional[TaskDue]
44
+ url: Optional[str] = None
45
+ duration: Optional[Any]
46
+ deadline: Optional[Any]
@@ -0,0 +1,116 @@
1
+ import json
2
+ from .models import Task, TodoistModel, TaskUpdate
3
+ from .exception import TodoistException
4
+ import requests
5
+ from typing import List, NoReturn, Any
6
+
7
+
8
+ def get_tasks(api_token: str, base_url: str) -> List[TodoistModel] | NoReturn:
9
+ """
10
+ Fetches active tasks from Todoist using the REST API directly (without SDK).
11
+ """
12
+
13
+ endpoint = "tasks"
14
+ url = f"{base_url}{endpoint}"
15
+
16
+ headers = {
17
+ "Authorization": f"Bearer {api_token}",
18
+ "Content-Type": "application/json",
19
+ }
20
+
21
+ try:
22
+ response = requests.get(url, headers=headers)
23
+ response.raise_for_status()
24
+ data = response.json()
25
+ tasks_data = data.get("results", data)
26
+
27
+ tasks = [TodoistModel(**task) for task in tasks_data]
28
+ return tasks
29
+ except requests.exceptions.RequestException as e:
30
+ raise TodoistException("query failed")
31
+ except json.JSONDecodeError as e:
32
+ raise TodoistException("json decode error")
33
+ except Exception as e:
34
+ raise TodoistException(f"Error: {e} {e.__class__.__name__}")
35
+
36
+
37
+ def get_task_by_id(api_token: str, base_url: str, task_id: str) -> TodoistModel:
38
+ """
39
+ Fetches a single task by its ID from Todoist.
40
+ """
41
+ endpoint = f"tasks/{task_id}"
42
+ url = f"{base_url}{endpoint}"
43
+
44
+ headers = {
45
+ "Authorization": f"Bearer {api_token}",
46
+ "Content-Type": "application/json",
47
+ }
48
+
49
+ try:
50
+ response = requests.get(url, headers=headers)
51
+ response.raise_for_status()
52
+ task_data = response.json()
53
+ return TodoistModel(**task_data)
54
+ except requests.exceptions.RequestException as e:
55
+ raise e
56
+ except json.JSONDecodeError as e:
57
+ raise Exception(f"Error decoding JSON response: {e}")
58
+ except Exception as e:
59
+ raise e
60
+
61
+
62
+ def add_task(api_token: str, base_url: str, task: Task) -> Any | NoReturn:
63
+ """
64
+ Creates a new task in Todoist using the REST API directly (without SDK).
65
+ """
66
+ endpoint = "tasks"
67
+ url = f"{base_url}{endpoint}"
68
+
69
+ headers = {
70
+ "Authorization": f"Bearer {api_token}",
71
+ "Content-Type": "application/json",
72
+ }
73
+ data = task.dict()
74
+
75
+ try:
76
+ response = requests.post(url, headers=headers, data=json.dumps(data))
77
+ response.raise_for_status() # Raise an exception for HTTP errors (4xx or 5xx)
78
+ task_data = response.json()
79
+ return TodoistModel(**task_data)
80
+ except requests.exceptions.RequestException as e:
81
+ raise TodoistException("mutation failed")
82
+ except json.JSONDecodeError as e:
83
+ raise TodoistException(" json decode error")
84
+ except Exception as e:
85
+ raise TodoistException(f"Error: {e} {e.__class__.__name__}")
86
+
87
+
88
+ def update_task(
89
+ api_token: str, base_url: str, task_id: str, task: TaskUpdate
90
+ ) -> TodoistModel:
91
+ """
92
+ Updates a task in Todoist using the REST API directly (without SDK).
93
+ """
94
+ endpoint = f"tasks/{task_id}"
95
+ url = f"{base_url}{endpoint}"
96
+
97
+ headers = {
98
+ "Authorization": f"Bearer {api_token}",
99
+ "Content-Type": "application/json",
100
+ }
101
+ data = task.dict(exclude_unset=True)
102
+
103
+ try:
104
+ response = requests.post(url, headers=headers, data=json.dumps(data))
105
+ response.raise_for_status() # Raise an exception for HTTP errors (4xx or 5xx)
106
+ # The update endpoint returns 204 No Content on success, so we fetch the task again to return it
107
+ if response.status_code == 204:
108
+ return get_task_by_id(api_token, base_url, task_id)
109
+ task_data = response.json()
110
+ return TodoistModel(**task_data)
111
+ except requests.exceptions.RequestException as e:
112
+ raise e
113
+ except json.JSONDecodeError as e:
114
+ raise Exception(f"Error decoding JSON response: {e}")
115
+ except Exception as e:
116
+ raise e
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: todoist-cli-tool
3
+ Version: 0.1.0
4
+ Summary: Todoist CLI - Manage your tasks from the command line
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: pydantic>=2.12.3
8
+ Requires-Dist: python-dotenv>=1.1.1
9
+ Requires-Dist: questionary>=2.1.1
10
+ Requires-Dist: requests>=2.32.4
11
+ Requires-Dist: rich>=14.0.0
12
+ Requires-Dist: textual>=3.3.0
13
+ Requires-Dist: typer>=0.20.0
14
+
15
+
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/__init__.py
4
+ src/exception.py
5
+ src/models.py
6
+ src/todoist.py
7
+ src/todoist_cli_tool.egg-info/PKG-INFO
8
+ src/todoist_cli_tool.egg-info/SOURCES.txt
9
+ src/todoist_cli_tool.egg-info/dependency_links.txt
10
+ src/todoist_cli_tool.egg-info/entry_points.txt
11
+ src/todoist_cli_tool.egg-info/requires.txt
12
+ src/todoist_cli_tool.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ todoist = main:app
@@ -0,0 +1,7 @@
1
+ pydantic>=2.12.3
2
+ python-dotenv>=1.1.1
3
+ questionary>=2.1.1
4
+ requests>=2.32.4
5
+ rich>=14.0.0
6
+ textual>=3.3.0
7
+ typer>=0.20.0
@@ -0,0 +1,4 @@
1
+ __init__
2
+ exception
3
+ models
4
+ todoist