google-api-client-wrapper 1.0.0__py3-none-any.whl
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.
- google_api_client_wrapper-1.0.0.dist-info/METADATA +103 -0
- google_api_client_wrapper-1.0.0.dist-info/RECORD +39 -0
- google_api_client_wrapper-1.0.0.dist-info/WHEEL +5 -0
- google_api_client_wrapper-1.0.0.dist-info/licenses/LICENSE +21 -0
- google_api_client_wrapper-1.0.0.dist-info/top_level.txt +1 -0
- google_client/__init__.py +6 -0
- google_client/services/__init__.py +13 -0
- google_client/services/calendar/__init__.py +14 -0
- google_client/services/calendar/api_service.py +454 -0
- google_client/services/calendar/constants.py +48 -0
- google_client/services/calendar/exceptions.py +35 -0
- google_client/services/calendar/query_builder.py +314 -0
- google_client/services/calendar/types.py +403 -0
- google_client/services/calendar/utils.py +338 -0
- google_client/services/drive/__init__.py +13 -0
- google_client/services/drive/api_service.py +1133 -0
- google_client/services/drive/constants.py +37 -0
- google_client/services/drive/exceptions.py +60 -0
- google_client/services/drive/query_builder.py +385 -0
- google_client/services/drive/types.py +242 -0
- google_client/services/drive/utils.py +392 -0
- google_client/services/gmail/__init__.py +16 -0
- google_client/services/gmail/api_service.py +715 -0
- google_client/services/gmail/constants.py +6 -0
- google_client/services/gmail/exceptions.py +45 -0
- google_client/services/gmail/query_builder.py +408 -0
- google_client/services/gmail/types.py +285 -0
- google_client/services/gmail/utils.py +426 -0
- google_client/services/tasks/__init__.py +12 -0
- google_client/services/tasks/api_service.py +561 -0
- google_client/services/tasks/constants.py +32 -0
- google_client/services/tasks/exceptions.py +35 -0
- google_client/services/tasks/query_builder.py +324 -0
- google_client/services/tasks/types.py +156 -0
- google_client/services/tasks/utils.py +224 -0
- google_client/user_client.py +208 -0
- google_client/utils/__init__.py +0 -0
- google_client/utils/datetime.py +144 -0
- google_client/utils/validation.py +71 -0
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
from datetime import datetime, date
|
|
2
|
+
from typing import Optional, List, Any, Dict
|
|
3
|
+
|
|
4
|
+
from googleapiclient.errors import HttpError
|
|
5
|
+
|
|
6
|
+
from .types import Task, TaskList
|
|
7
|
+
from . import utils
|
|
8
|
+
from .constants import (
|
|
9
|
+
DEFAULT_MAX_RESULTS, MAX_RESULTS_LIMIT, DEFAULT_TASK_LIST_ID,
|
|
10
|
+
TASK_STATUS_COMPLETED, TASK_STATUS_NEEDS_ACTION
|
|
11
|
+
)
|
|
12
|
+
from .exceptions import (
|
|
13
|
+
TasksError, TasksPermissionError, TasksNotFoundError,
|
|
14
|
+
InvalidTaskDataError, TaskMoveError
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TasksApiService:
|
|
19
|
+
"""
|
|
20
|
+
Service layer for Tasks API operations.
|
|
21
|
+
Contains all Tasks API functionality that was removed from dataclasses.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, service: Any):
|
|
25
|
+
"""
|
|
26
|
+
Initialize Tasks service.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
service: The Tasks API service instance
|
|
30
|
+
"""
|
|
31
|
+
self._service = service
|
|
32
|
+
|
|
33
|
+
def query(self):
|
|
34
|
+
"""
|
|
35
|
+
Create a new TaskQueryBuilder for building complex task queries with a fluent API.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
TaskQueryBuilder instance for method chaining
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
tasks = (user.tasks.query()
|
|
42
|
+
.limit(50)
|
|
43
|
+
.due_today()
|
|
44
|
+
.show_completed(False)
|
|
45
|
+
.in_task_list("my_list_id")
|
|
46
|
+
.execute())
|
|
47
|
+
"""
|
|
48
|
+
from .query_builder import TaskQueryBuilder
|
|
49
|
+
return TaskQueryBuilder(self)
|
|
50
|
+
|
|
51
|
+
# Task Operations
|
|
52
|
+
def list_tasks(
|
|
53
|
+
self,
|
|
54
|
+
task_list_id: str = DEFAULT_TASK_LIST_ID,
|
|
55
|
+
max_results: Optional[int] = DEFAULT_MAX_RESULTS,
|
|
56
|
+
completed_min: Optional[datetime] = None,
|
|
57
|
+
completed_max: Optional[datetime] = None,
|
|
58
|
+
due_min: Optional[datetime] = None,
|
|
59
|
+
due_max: Optional[datetime] = None,
|
|
60
|
+
show_completed: Optional[bool] = None,
|
|
61
|
+
show_hidden: Optional[bool] = None
|
|
62
|
+
) -> List[Task]:
|
|
63
|
+
"""
|
|
64
|
+
Fetches a list of tasks from Google Tasks with optional filtering.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
task_list_id: Task list identifier (default: '@default').
|
|
68
|
+
max_results: Maximum number of tasks to retrieve.
|
|
69
|
+
completed_min: Lower bound for a task's completion date (RFC 3339).
|
|
70
|
+
completed_max: Upper bound for a task's completion date (RFC 3339).
|
|
71
|
+
due_min: Lower bound for a task's due date (RFC 3339).
|
|
72
|
+
due_max: Upper bound for a task's due date (RFC 3339).
|
|
73
|
+
show_completed: Flag indicating whether completed tasks are returned.
|
|
74
|
+
show_hidden: Flag indicating whether hidden tasks are returned.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A list of Task objects representing the tasks found.
|
|
78
|
+
"""
|
|
79
|
+
# Input validation
|
|
80
|
+
if max_results and (max_results < 1 or max_results > MAX_RESULTS_LIMIT):
|
|
81
|
+
raise ValueError(f"max_results must be between 1 and {MAX_RESULTS_LIMIT}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
# Build request parameters
|
|
86
|
+
request_params = {
|
|
87
|
+
'tasklist': task_list_id,
|
|
88
|
+
'maxResults': max_results
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Add optional filters
|
|
92
|
+
if completed_min:
|
|
93
|
+
request_params['completedMin'] = completed_min.isoformat() + 'Z'
|
|
94
|
+
if completed_max:
|
|
95
|
+
request_params['completedMax'] = completed_max.isoformat() + 'Z'
|
|
96
|
+
if due_min:
|
|
97
|
+
request_params['dueMin'] = due_min.isoformat() + 'Z'
|
|
98
|
+
if due_max:
|
|
99
|
+
request_params['dueMax'] = due_max.isoformat() + 'Z'
|
|
100
|
+
if show_completed is not None:
|
|
101
|
+
request_params['showCompleted'] = show_completed
|
|
102
|
+
if show_hidden is not None:
|
|
103
|
+
request_params['showHidden'] = show_hidden
|
|
104
|
+
|
|
105
|
+
# Make API call
|
|
106
|
+
result = self._service.tasks().list(**request_params).execute()
|
|
107
|
+
tasks_data = result.get('items', [])
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# Parse tasks
|
|
111
|
+
tasks = []
|
|
112
|
+
for task_data in tasks_data:
|
|
113
|
+
try:
|
|
114
|
+
tasks.append(utils.from_google_task(task_data, task_list_id))
|
|
115
|
+
except Exception as e:
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
return tasks
|
|
119
|
+
|
|
120
|
+
except HttpError as e:
|
|
121
|
+
if e.resp.status == 403:
|
|
122
|
+
raise TasksPermissionError(f"Permission denied: {e}")
|
|
123
|
+
elif e.resp.status == 404:
|
|
124
|
+
raise TasksNotFoundError(f"Task list not found: {task_list_id}")
|
|
125
|
+
else:
|
|
126
|
+
raise TasksError(f"Tasks API error listing tasks: {e}")
|
|
127
|
+
except Exception as e:
|
|
128
|
+
raise TasksError(f"Unexpected error listing tasks: {e}")
|
|
129
|
+
|
|
130
|
+
def get_task(self, task_id: str, task_list_id: str = DEFAULT_TASK_LIST_ID) -> Task:
|
|
131
|
+
"""
|
|
132
|
+
Retrieves a specific task from Google Tasks using its unique identifier.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
task_list_id: The task list identifier containing the task.
|
|
136
|
+
task_id: The unique identifier of the task to be retrieved.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
A Task object representing the task with the specified ID.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
task_data = self._service.tasks().get(
|
|
144
|
+
tasklist=task_list_id,
|
|
145
|
+
task=task_id
|
|
146
|
+
).execute()
|
|
147
|
+
|
|
148
|
+
return utils.from_google_task(task_data, task_list_id)
|
|
149
|
+
|
|
150
|
+
except HttpError as e:
|
|
151
|
+
if e.resp.status == 404:
|
|
152
|
+
raise TasksNotFoundError(f"Task not found: {task_id}")
|
|
153
|
+
elif e.resp.status == 403:
|
|
154
|
+
raise TasksPermissionError(f"Permission denied accessing task: {e}")
|
|
155
|
+
else:
|
|
156
|
+
raise TasksError(f"Tasks API error getting task {task_id}: {e}")
|
|
157
|
+
except Exception as e:
|
|
158
|
+
raise TasksError(f"Unexpected error getting task: {e}")
|
|
159
|
+
|
|
160
|
+
def create_task(
|
|
161
|
+
self,
|
|
162
|
+
title: str,
|
|
163
|
+
task_list_id: str = DEFAULT_TASK_LIST_ID,
|
|
164
|
+
notes: Optional[str] = None,
|
|
165
|
+
due: Optional[date] = None,
|
|
166
|
+
parent: Optional[str] = None,
|
|
167
|
+
position: Optional[str] = None
|
|
168
|
+
) -> Task:
|
|
169
|
+
"""
|
|
170
|
+
Creates a new task.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
title: The title of the task.
|
|
174
|
+
task_list_id: Task list identifier (default: '@default').
|
|
175
|
+
notes: Notes describing the task.
|
|
176
|
+
due: Due date of the task.
|
|
177
|
+
parent: Parent task identifier.
|
|
178
|
+
position: Position in the task list.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
A Task object representing the created task.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
# Create task body using utils
|
|
186
|
+
task_body = utils.create_task_body(
|
|
187
|
+
title=title,
|
|
188
|
+
notes=notes,
|
|
189
|
+
due=due,
|
|
190
|
+
parent=parent,
|
|
191
|
+
position=position
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Make API call
|
|
195
|
+
created_task = self._service.tasks().insert(
|
|
196
|
+
tasklist=task_list_id,
|
|
197
|
+
body=task_body
|
|
198
|
+
).execute()
|
|
199
|
+
|
|
200
|
+
task = utils.from_google_task(created_task, task_list_id)
|
|
201
|
+
return task
|
|
202
|
+
|
|
203
|
+
except HttpError as e:
|
|
204
|
+
if e.resp.status == 403:
|
|
205
|
+
raise TasksPermissionError(f"Permission denied creating task: {e}")
|
|
206
|
+
elif e.resp.status == 404:
|
|
207
|
+
raise TasksNotFoundError(f"Task list not found: {task_list_id}")
|
|
208
|
+
else:
|
|
209
|
+
raise TasksError(f"Tasks API error creating task: {e}")
|
|
210
|
+
except ValueError as e:
|
|
211
|
+
raise InvalidTaskDataError(f"Invalid task data: {e}")
|
|
212
|
+
except Exception as e:
|
|
213
|
+
raise TasksError(f"Unexpected error creating task: {e}")
|
|
214
|
+
|
|
215
|
+
def update_task(self, task: Task, task_list_id: str = DEFAULT_TASK_LIST_ID) -> Task:
|
|
216
|
+
"""
|
|
217
|
+
Updates an existing task.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
task: The task to update.
|
|
221
|
+
task_list_id: Task list identifier containing the task.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
A Task object representing the updated task.
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
# Build update body
|
|
229
|
+
task_body = task.to_dict()
|
|
230
|
+
|
|
231
|
+
# Make API call
|
|
232
|
+
updated_task = self._service.tasks().update(
|
|
233
|
+
tasklist=task_list_id,
|
|
234
|
+
task=task.task_id,
|
|
235
|
+
body=task_body
|
|
236
|
+
).execute()
|
|
237
|
+
|
|
238
|
+
task = utils.from_google_task(updated_task, task_list_id)
|
|
239
|
+
return task
|
|
240
|
+
|
|
241
|
+
except HttpError as e:
|
|
242
|
+
if e.resp.status == 404:
|
|
243
|
+
raise TasksNotFoundError(f"Task not found: {task.task_id}")
|
|
244
|
+
elif e.resp.status == 403:
|
|
245
|
+
raise TasksPermissionError(f"Permission denied updating task: {e}")
|
|
246
|
+
else:
|
|
247
|
+
raise TasksError(f"Tasks API error updating task {task.task_id}: {e}")
|
|
248
|
+
except ValueError as e:
|
|
249
|
+
raise InvalidTaskDataError(f"Invalid task data: {e}")
|
|
250
|
+
except Exception as e:
|
|
251
|
+
raise TasksError(f"Unexpected error updating task: {e}")
|
|
252
|
+
|
|
253
|
+
def delete_task(self, task: Task, task_list_id: str = DEFAULT_TASK_LIST_ID) -> bool:
|
|
254
|
+
"""
|
|
255
|
+
Deletes a task.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
task: The task to delete.
|
|
259
|
+
task_list_id: Task list identifier containing the task.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
True if the operation was successful.
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
try:
|
|
266
|
+
self._service.tasks().delete(
|
|
267
|
+
tasklist=task_list_id,
|
|
268
|
+
task=task.task_id
|
|
269
|
+
).execute()
|
|
270
|
+
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
except HttpError as e:
|
|
274
|
+
if e.resp.status == 404:
|
|
275
|
+
raise TasksNotFoundError(f"Task not found: {task.task_id}")
|
|
276
|
+
elif e.resp.status == 403:
|
|
277
|
+
raise TasksPermissionError(f"Permission denied deleting task: {e}")
|
|
278
|
+
else:
|
|
279
|
+
raise TasksError(f"Tasks API error deleting task {task.task_id}: {e}")
|
|
280
|
+
except Exception as e:
|
|
281
|
+
raise TasksError(f"Unexpected error deleting task: {e}")
|
|
282
|
+
|
|
283
|
+
def move_task(
|
|
284
|
+
self,
|
|
285
|
+
task: Task,
|
|
286
|
+
task_list_id: str = DEFAULT_TASK_LIST_ID,
|
|
287
|
+
parent: Optional[str] = None,
|
|
288
|
+
previous: Optional[str] = None
|
|
289
|
+
) -> Task:
|
|
290
|
+
"""
|
|
291
|
+
Moves a task to a different position in the task list.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
task: The task to move.
|
|
295
|
+
task_list_id: Task list identifier containing the task.
|
|
296
|
+
parent: Parent task identifier (optional).
|
|
297
|
+
previous: Previous sibling task identifier (optional).
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
A Task object representing the moved task.
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
request_params = {
|
|
305
|
+
'tasklist': task_list_id,
|
|
306
|
+
'task': task.task_id
|
|
307
|
+
}
|
|
308
|
+
if parent:
|
|
309
|
+
request_params['parent'] = parent
|
|
310
|
+
if previous:
|
|
311
|
+
request_params['previous'] = previous
|
|
312
|
+
|
|
313
|
+
moved_task = self._service.tasks().move(**request_params).execute()
|
|
314
|
+
|
|
315
|
+
task = utils.from_google_task(moved_task, task_list_id)
|
|
316
|
+
return task
|
|
317
|
+
|
|
318
|
+
except HttpError as e:
|
|
319
|
+
if e.resp.status == 404:
|
|
320
|
+
raise TasksNotFoundError(f"Task not found: {task.task_id}")
|
|
321
|
+
elif e.resp.status == 403:
|
|
322
|
+
raise TasksPermissionError(f"Permission denied moving task: {e}")
|
|
323
|
+
else:
|
|
324
|
+
raise TaskMoveError(f"Tasks API error moving task {task.task_id}: {e}")
|
|
325
|
+
except Exception as e:
|
|
326
|
+
raise TaskMoveError(f"Unexpected error moving task: {e}")
|
|
327
|
+
|
|
328
|
+
def mark_completed(self, task: Task, task_list_id: str = DEFAULT_TASK_LIST_ID) -> Task:
|
|
329
|
+
"""
|
|
330
|
+
Marks a task as completed.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
task: The task to mark as completed.
|
|
334
|
+
task_list_id: Task list identifier containing the task.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
A Task object representing the updated task.
|
|
338
|
+
"""
|
|
339
|
+
task.status = TASK_STATUS_COMPLETED
|
|
340
|
+
task.completed = date.today()
|
|
341
|
+
return self.update_task(task=task, task_list_id=task_list_id)
|
|
342
|
+
|
|
343
|
+
def mark_incomplete(self, task: Task, task_list_id: str = DEFAULT_TASK_LIST_ID) -> Task:
|
|
344
|
+
"""
|
|
345
|
+
Marks a task as needing action (incomplete).
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
task: The task to mark as incomplete.
|
|
349
|
+
task_list_id: Task list identifier containing the task.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
A Task object representing the updated task.
|
|
353
|
+
"""
|
|
354
|
+
task.completed = None
|
|
355
|
+
task.status = TASK_STATUS_NEEDS_ACTION
|
|
356
|
+
return self.update_task(task=task, task_list_id=task_list_id)
|
|
357
|
+
|
|
358
|
+
# Task List Operations
|
|
359
|
+
def list_task_lists(self) -> List[TaskList]:
|
|
360
|
+
"""
|
|
361
|
+
Fetches a list of task lists from Google Tasks.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
A list of TaskList objects representing the task lists found.
|
|
365
|
+
"""
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
result = self._service.tasklists().list().execute()
|
|
369
|
+
task_lists_data = result.get('items', [])
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# Parse task lists
|
|
373
|
+
task_lists = []
|
|
374
|
+
for task_list_data in task_lists_data:
|
|
375
|
+
try:
|
|
376
|
+
task_lists.append(utils.from_google_task_list(task_list_data))
|
|
377
|
+
except Exception as e:
|
|
378
|
+
pass
|
|
379
|
+
|
|
380
|
+
return task_lists
|
|
381
|
+
|
|
382
|
+
except HttpError as e:
|
|
383
|
+
if e.resp.status == 403:
|
|
384
|
+
raise TasksPermissionError(f"Permission denied: {e}")
|
|
385
|
+
else:
|
|
386
|
+
raise TasksError(f"Tasks API error listing task lists: {e}")
|
|
387
|
+
except Exception as e:
|
|
388
|
+
raise TasksError(f"Unexpected error listing task lists: {e}")
|
|
389
|
+
|
|
390
|
+
def get_task_list(self, task_list_id: str) -> TaskList:
|
|
391
|
+
"""
|
|
392
|
+
Retrieves a specific task list from Google Tasks.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
task_list_id: The unique identifier of the task list.
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
A TaskList object representing the task list with the specified ID.
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
task_list_data = self._service.tasklists().get(
|
|
403
|
+
tasklist=task_list_id
|
|
404
|
+
).execute()
|
|
405
|
+
|
|
406
|
+
return utils.from_google_task_list(task_list_data)
|
|
407
|
+
|
|
408
|
+
except HttpError as e:
|
|
409
|
+
if e.resp.status == 404:
|
|
410
|
+
raise TasksNotFoundError(f"Task list not found: {task_list_id}")
|
|
411
|
+
elif e.resp.status == 403:
|
|
412
|
+
raise TasksPermissionError(f"Permission denied accessing task list: {e}")
|
|
413
|
+
else:
|
|
414
|
+
raise TasksError(f"Tasks API error getting task list {task_list_id}: {e}")
|
|
415
|
+
except Exception as e:
|
|
416
|
+
raise TasksError(f"Unexpected error getting task list: {e}")
|
|
417
|
+
|
|
418
|
+
def create_task_list(self, title: str) -> TaskList:
|
|
419
|
+
"""
|
|
420
|
+
Creates a new task list.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
title: The title of the task list.
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
A TaskList object representing the created task list.
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
try:
|
|
430
|
+
# Create task list body using utils
|
|
431
|
+
task_list_body = utils.create_task_list_body(title)
|
|
432
|
+
|
|
433
|
+
# Make API call
|
|
434
|
+
created_task_list = self._service.tasklists().insert(
|
|
435
|
+
body=task_list_body
|
|
436
|
+
).execute()
|
|
437
|
+
|
|
438
|
+
task_list = utils.from_google_task_list(created_task_list)
|
|
439
|
+
return task_list
|
|
440
|
+
|
|
441
|
+
except HttpError as e:
|
|
442
|
+
if e.resp.status == 403:
|
|
443
|
+
raise TasksPermissionError(f"Permission denied creating task list: {e}")
|
|
444
|
+
else:
|
|
445
|
+
raise TasksError(f"Tasks API error creating task list: {e}")
|
|
446
|
+
except ValueError as e:
|
|
447
|
+
raise InvalidTaskDataError(f"Invalid task list data: {e}")
|
|
448
|
+
except Exception as e:
|
|
449
|
+
raise TasksError(f"Unexpected error creating task list: {e}")
|
|
450
|
+
|
|
451
|
+
def update_task_list(self, task_list: TaskList, title: str) -> TaskList:
|
|
452
|
+
"""
|
|
453
|
+
Updates an existing task list.
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
task_list: The task list to update.
|
|
457
|
+
title: New title for the task list.
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
A TaskList object representing the updated task list.
|
|
461
|
+
"""
|
|
462
|
+
|
|
463
|
+
try:
|
|
464
|
+
# Create update body
|
|
465
|
+
task_list_body = utils.create_task_list_body(title)
|
|
466
|
+
task_list_body['id'] = task_list.task_list_id
|
|
467
|
+
|
|
468
|
+
# Make API call
|
|
469
|
+
updated_task_list = self._service.tasklists().update(
|
|
470
|
+
tasklist=task_list.task_list_id,
|
|
471
|
+
body=task_list_body
|
|
472
|
+
).execute()
|
|
473
|
+
|
|
474
|
+
task_list.title = title
|
|
475
|
+
task_list = utils.from_google_task_list(updated_task_list)
|
|
476
|
+
return task_list
|
|
477
|
+
|
|
478
|
+
except HttpError as e:
|
|
479
|
+
if e.resp.status == 404:
|
|
480
|
+
raise TasksNotFoundError(f"Task list not found: {task_list.task_list_id}")
|
|
481
|
+
elif e.resp.status == 403:
|
|
482
|
+
raise TasksPermissionError(f"Permission denied updating task list: {e}")
|
|
483
|
+
else:
|
|
484
|
+
raise TasksError(f"Tasks API error updating task list {task_list.task_list_id}: {e}")
|
|
485
|
+
except ValueError as e:
|
|
486
|
+
raise InvalidTaskDataError(f"Invalid task list data: {e}")
|
|
487
|
+
except Exception as e:
|
|
488
|
+
raise TasksError(f"Unexpected error updating task list: {e}")
|
|
489
|
+
|
|
490
|
+
def delete_task_list(self, task_list: TaskList) -> bool:
|
|
491
|
+
"""
|
|
492
|
+
Deletes a task list.
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
task_list: The task list to delete.
|
|
496
|
+
|
|
497
|
+
Returns:
|
|
498
|
+
True if the operation was successful.
|
|
499
|
+
"""
|
|
500
|
+
|
|
501
|
+
try:
|
|
502
|
+
self._service.tasklists().delete(
|
|
503
|
+
tasklist=task_list.task_list_id
|
|
504
|
+
).execute()
|
|
505
|
+
|
|
506
|
+
return True
|
|
507
|
+
|
|
508
|
+
except HttpError as e:
|
|
509
|
+
if e.resp.status == 404:
|
|
510
|
+
raise TasksNotFoundError(f"Task list not found: {task_list.task_list_id}")
|
|
511
|
+
elif e.resp.status == 403:
|
|
512
|
+
raise TasksPermissionError(f"Permission denied deleting task list: {e}")
|
|
513
|
+
elif e.resp.status == 400:
|
|
514
|
+
raise TasksError(f"Cannot delete default task list: {task_list.task_list_id}")
|
|
515
|
+
else:
|
|
516
|
+
raise TasksError(f"Tasks API error deleting task list {task_list.task_list_id}: {e}")
|
|
517
|
+
except Exception as e:
|
|
518
|
+
raise TasksError(f"Unexpected error deleting task list: {e}")
|
|
519
|
+
|
|
520
|
+
# Batch Operations
|
|
521
|
+
def batch_get_tasks(self, task_list_id: str, task_ids: List[str]) -> List[Task]:
|
|
522
|
+
"""
|
|
523
|
+
Retrieves multiple tasks by their IDs.
|
|
524
|
+
|
|
525
|
+
Args:
|
|
526
|
+
task_list_id: Task list identifier containing the tasks.
|
|
527
|
+
task_ids: List of task IDs to retrieve.
|
|
528
|
+
|
|
529
|
+
Returns:
|
|
530
|
+
List of Task objects.
|
|
531
|
+
"""
|
|
532
|
+
|
|
533
|
+
tasks = []
|
|
534
|
+
for task_id in task_ids:
|
|
535
|
+
try:
|
|
536
|
+
tasks.append(self.get_task(task_list_id, task_id))
|
|
537
|
+
except Exception as e:
|
|
538
|
+
pass
|
|
539
|
+
|
|
540
|
+
return tasks
|
|
541
|
+
|
|
542
|
+
def batch_create_tasks(self, tasks_data: List[Dict[str, Any]], task_list_id: str = DEFAULT_TASK_LIST_ID) -> List[Task]:
|
|
543
|
+
"""
|
|
544
|
+
Creates multiple tasks.
|
|
545
|
+
|
|
546
|
+
Args:
|
|
547
|
+
task_list_id: Task list identifier to create tasks in.
|
|
548
|
+
tasks_data: List of dictionaries containing task parameters.
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
List of created Task objects.
|
|
552
|
+
"""
|
|
553
|
+
|
|
554
|
+
created_tasks = []
|
|
555
|
+
for task_data in tasks_data:
|
|
556
|
+
try:
|
|
557
|
+
created_tasks.append(self.create_task(task_list_id=task_list_id, **task_data))
|
|
558
|
+
except Exception as e:
|
|
559
|
+
pass
|
|
560
|
+
|
|
561
|
+
return created_tasks
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# API Limits and Defaults
|
|
2
|
+
MAX_RESULTS_LIMIT = 100
|
|
3
|
+
DEFAULT_MAX_RESULTS = 100
|
|
4
|
+
|
|
5
|
+
# Field Length Limits
|
|
6
|
+
MAX_TITLE_LENGTH = 1024
|
|
7
|
+
MAX_NOTES_LENGTH = 8192
|
|
8
|
+
|
|
9
|
+
# Task Status Options
|
|
10
|
+
TASK_STATUS_NEEDS_ACTION = "needsAction"
|
|
11
|
+
TASK_STATUS_COMPLETED = "completed"
|
|
12
|
+
|
|
13
|
+
VALID_TASK_STATUSES = [
|
|
14
|
+
TASK_STATUS_NEEDS_ACTION,
|
|
15
|
+
TASK_STATUS_COMPLETED
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
# Default Task List
|
|
19
|
+
DEFAULT_TASK_LIST_ID = "@default"
|
|
20
|
+
|
|
21
|
+
# API Parameter Names
|
|
22
|
+
API_PARAM_COMPLETED_MIN = "completedMin"
|
|
23
|
+
API_PARAM_COMPLETED_MAX = "completedMax"
|
|
24
|
+
API_PARAM_DUE_MIN = "dueMin"
|
|
25
|
+
API_PARAM_DUE_MAX = "dueMax"
|
|
26
|
+
API_PARAM_SHOW_COMPLETED = "showCompleted"
|
|
27
|
+
API_PARAM_SHOW_HIDDEN = "showHidden"
|
|
28
|
+
API_PARAM_MAX_RESULTS = "maxResults"
|
|
29
|
+
|
|
30
|
+
# DateTime Formats
|
|
31
|
+
ISO_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
32
|
+
RFC3339_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class TasksError(Exception):
|
|
4
|
+
"""Base exception for Tasks API errors."""
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TasksNotFoundError(TasksError):
|
|
9
|
+
"""Raised when a task or task list is not found."""
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TasksPermissionError(TasksError):
|
|
14
|
+
"""Raised when the user lacks permission for a tasks operation."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TaskConflictError(TasksError):
|
|
19
|
+
"""Raised when there is a conflict with task operations."""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InvalidTaskDataError(TasksError):
|
|
24
|
+
"""Raised when task data is invalid or malformed."""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TaskListConflictError(TasksError):
|
|
29
|
+
"""Raised when there is a conflict with task list operations."""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TaskMoveError(TasksError):
|
|
34
|
+
"""Raised when there are issues with moving tasks."""
|
|
35
|
+
pass
|