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.
Files changed (39) hide show
  1. google_api_client_wrapper-1.0.0.dist-info/METADATA +103 -0
  2. google_api_client_wrapper-1.0.0.dist-info/RECORD +39 -0
  3. google_api_client_wrapper-1.0.0.dist-info/WHEEL +5 -0
  4. google_api_client_wrapper-1.0.0.dist-info/licenses/LICENSE +21 -0
  5. google_api_client_wrapper-1.0.0.dist-info/top_level.txt +1 -0
  6. google_client/__init__.py +6 -0
  7. google_client/services/__init__.py +13 -0
  8. google_client/services/calendar/__init__.py +14 -0
  9. google_client/services/calendar/api_service.py +454 -0
  10. google_client/services/calendar/constants.py +48 -0
  11. google_client/services/calendar/exceptions.py +35 -0
  12. google_client/services/calendar/query_builder.py +314 -0
  13. google_client/services/calendar/types.py +403 -0
  14. google_client/services/calendar/utils.py +338 -0
  15. google_client/services/drive/__init__.py +13 -0
  16. google_client/services/drive/api_service.py +1133 -0
  17. google_client/services/drive/constants.py +37 -0
  18. google_client/services/drive/exceptions.py +60 -0
  19. google_client/services/drive/query_builder.py +385 -0
  20. google_client/services/drive/types.py +242 -0
  21. google_client/services/drive/utils.py +392 -0
  22. google_client/services/gmail/__init__.py +16 -0
  23. google_client/services/gmail/api_service.py +715 -0
  24. google_client/services/gmail/constants.py +6 -0
  25. google_client/services/gmail/exceptions.py +45 -0
  26. google_client/services/gmail/query_builder.py +408 -0
  27. google_client/services/gmail/types.py +285 -0
  28. google_client/services/gmail/utils.py +426 -0
  29. google_client/services/tasks/__init__.py +12 -0
  30. google_client/services/tasks/api_service.py +561 -0
  31. google_client/services/tasks/constants.py +32 -0
  32. google_client/services/tasks/exceptions.py +35 -0
  33. google_client/services/tasks/query_builder.py +324 -0
  34. google_client/services/tasks/types.py +156 -0
  35. google_client/services/tasks/utils.py +224 -0
  36. google_client/user_client.py +208 -0
  37. google_client/utils/__init__.py +0 -0
  38. google_client/utils/datetime.py +144 -0
  39. google_client/utils/validation.py +71 -0
@@ -0,0 +1,48 @@
1
+ # API Limits and Defaults
2
+ MAX_RESULTS_LIMIT = 2500
3
+ DEFAULT_MAX_RESULTS = 100
4
+ DEFAULT_DAYS_AHEAD = 7
5
+
6
+ # Field Length Limits
7
+ MAX_SUMMARY_LENGTH = 1024
8
+ MAX_DESCRIPTION_LENGTH = 8192
9
+ MAX_LOCATION_LENGTH = 1024
10
+ MAX_QUERY_LENGTH = 500
11
+
12
+ # Event Status Options
13
+ EVENT_STATUS_CONFIRMED = "confirmed"
14
+ EVENT_STATUS_TENTATIVE = "tentative"
15
+ EVENT_STATUS_CANCELLED = "cancelled"
16
+
17
+ VALID_EVENT_STATUSES = [
18
+ EVENT_STATUS_CONFIRMED,
19
+ EVENT_STATUS_TENTATIVE,
20
+ EVENT_STATUS_CANCELLED
21
+ ]
22
+
23
+ # Attendee Response Status Options
24
+ RESPONSE_NEEDS_ACTION = "needsAction"
25
+ RESPONSE_DECLINED = "declined"
26
+ RESPONSE_TENTATIVE = "tentative"
27
+ RESPONSE_ACCEPTED = "accepted"
28
+
29
+ VALID_RESPONSE_STATUSES = [
30
+ RESPONSE_NEEDS_ACTION,
31
+ RESPONSE_DECLINED,
32
+ RESPONSE_TENTATIVE,
33
+ RESPONSE_ACCEPTED
34
+ ]
35
+
36
+ # Calendar Default Settings
37
+ DEFAULT_CALENDAR_ID = "primary"
38
+
39
+ # Free/Busy Query Settings
40
+ DEFAULT_FREEBUSY_DURATION_MINUTES = 60
41
+ MAX_FREEBUSY_DAYS_RANGE = 183 # 6 months maximum per Google's API limits
42
+ MAX_CALENDARS_PER_FREEBUSY_QUERY = 50
43
+ DEFAULT_GROUP_EXPANSION_MAX = 25
44
+ DEFAULT_CALENDAR_EXPANSION_MAX = 25
45
+
46
+ # Time Slot Settings
47
+ MIN_TIME_SLOT_DURATION_MINUTES = 15
48
+ MAX_TIME_SLOT_DURATION_MINUTES = 1440 # 24 hours
@@ -0,0 +1,35 @@
1
+
2
+
3
+ class CalendarError(Exception):
4
+ """Base exception for Calendar API errors."""
5
+ pass
6
+
7
+
8
+ class EventNotFoundError(CalendarError):
9
+ """Raised when a calendar event is not found."""
10
+ pass
11
+
12
+
13
+ class CalendarNotFoundError(CalendarError):
14
+ """Raised when a calendar is not found."""
15
+ pass
16
+
17
+
18
+ class CalendarPermissionError(CalendarError):
19
+ """Raised when the user lacks permission for a calendar operation."""
20
+ pass
21
+
22
+
23
+ class EventConflictError(CalendarError):
24
+ """Raised when there is a conflict with calendar event operations."""
25
+ pass
26
+
27
+
28
+ class InvalidEventDataError(CalendarError):
29
+ """Raised when event data is invalid or malformed."""
30
+ pass
31
+
32
+
33
+ class RecurrenceError(CalendarError):
34
+ """Raised when there are issues with recurring event operations."""
35
+ pass
@@ -0,0 +1,314 @@
1
+ from datetime import datetime, date, timedelta
2
+ from typing import Optional, List, TYPE_CHECKING
3
+ from ...utils.datetime import date_start, date_end, days_from_today
4
+ from .constants import MAX_RESULTS_LIMIT, MAX_QUERY_LENGTH, DEFAULT_MAX_RESULTS, DEFAULT_CALENDAR_ID
5
+
6
+ if TYPE_CHECKING:
7
+ from .types import CalendarEvent
8
+ from .api_service import CalendarApiService
9
+
10
+
11
+ class EventQueryBuilder:
12
+ """
13
+ Builder pattern for constructing calendar event queries with a fluent API.
14
+ Provides a clean, readable way to build complex event queries.
15
+
16
+ Example usage:
17
+ events = (CalendarEvent.query()
18
+ .limit(50)
19
+ .in_date_range(start_date, end_date)
20
+ .search("meeting")
21
+ .in_calendar("work@company.com")
22
+ .execute())
23
+ """
24
+
25
+ def __init__(self, api_service: "CalendarApiService"):
26
+ self._api_service = api_service
27
+ self._max_results: Optional[int] = DEFAULT_MAX_RESULTS
28
+ self._start: Optional[datetime] = None
29
+ self._end: Optional[datetime] = None
30
+ self._query: Optional[str] = None
31
+ self._calendar_id: str = DEFAULT_CALENDAR_ID
32
+ self._attendee_filter: Optional[str] = None
33
+ self._has_location_filter: Optional[bool] = None
34
+ self._single_events_only: bool = True
35
+
36
+ def limit(self, count: int) -> "EventQueryBuilder":
37
+ """
38
+ Set the maximum number of events to retrieve.
39
+ Args:
40
+ count: Maximum number of events (1-2500)
41
+ Returns:
42
+ Self for method chaining
43
+ """
44
+ if count < 1 or count > MAX_RESULTS_LIMIT:
45
+ raise ValueError(f"Limit must be between 1 and {MAX_RESULTS_LIMIT}")
46
+ self._max_results = count
47
+ return self
48
+
49
+ def from_date(self, start: datetime) -> "EventQueryBuilder":
50
+ """
51
+ Set the start date/time for the query.
52
+ Args:
53
+ start: Start datetime
54
+ Returns:
55
+ Self for method chaining
56
+ """
57
+ self._start = start
58
+ return self
59
+
60
+ def to_date(self, end: datetime) -> "EventQueryBuilder":
61
+ """
62
+ Set the end date/time for the query.
63
+ Args:
64
+ end: End datetime
65
+ Returns:
66
+ Self for method chaining
67
+ """
68
+ self._end = end
69
+ return self
70
+
71
+ def in_date_range(self, start: datetime, end: datetime) -> "EventQueryBuilder":
72
+ """
73
+ Set both start and end dates for the query.
74
+ Args:
75
+ start: Start datetime
76
+ end: End datetime
77
+ Returns:
78
+ Self for method chaining
79
+ """
80
+ if start >= end:
81
+ raise ValueError("Start date must be before end date")
82
+ self._start = start
83
+ self._end = end
84
+ return self
85
+
86
+ def search(self, query: str) -> "EventQueryBuilder":
87
+ """
88
+ Add a text search query to filter events.
89
+ Args:
90
+ query: Search string for event content
91
+ Returns:
92
+ Self for method chaining
93
+ """
94
+ if len(query) > MAX_QUERY_LENGTH:
95
+ raise ValueError(f"Query string cannot exceed {MAX_QUERY_LENGTH} characters")
96
+ self._query = query
97
+ return self
98
+
99
+ def in_calendar(self, calendar_id: str) -> "EventQueryBuilder":
100
+ """
101
+ Specify which calendar to query.
102
+ Args:
103
+ calendar_id: Calendar identifier
104
+ Returns:
105
+ Self for method chaining
106
+ """
107
+ self._calendar_id = calendar_id
108
+ return self
109
+
110
+ def by_attendee(self, email: str) -> "EventQueryBuilder":
111
+ """
112
+ Filter events by attendee email.
113
+ Args:
114
+ email: Attendee email address
115
+ Returns:
116
+ Self for method chaining
117
+ """
118
+ self._attendee_filter = email
119
+ return self
120
+
121
+ def with_location(self) -> "EventQueryBuilder":
122
+ """
123
+ Filter to only events that have a location specified.
124
+ Returns:
125
+ Self for method chaining
126
+ """
127
+ self._has_location_filter = True
128
+ return self
129
+
130
+ def without_location(self) -> "EventQueryBuilder":
131
+ """
132
+ Filter to only events that do not have a location specified.
133
+ Returns:
134
+ Self for method chaining
135
+ """
136
+ self._has_location_filter = False
137
+ return self
138
+
139
+ # Convenience date methods
140
+ def today(self) -> "EventQueryBuilder":
141
+ """
142
+ Filter to events happening today.
143
+ Returns:
144
+ Self for method chaining
145
+ """
146
+ today = date.today()
147
+ start_of_day = date_start(today)
148
+ end_of_day = date_end(today)
149
+ return self.in_date_range(start_of_day, end_of_day)
150
+
151
+ def tomorrow(self) -> "EventQueryBuilder":
152
+ """
153
+ Filter to events happening tomorrow.
154
+ Returns:
155
+ Self for method chaining
156
+ """
157
+ tomorrow = date.today() + timedelta(days=1)
158
+ start_of_day = date_start(tomorrow)
159
+ end_of_day = date_end(tomorrow)
160
+ return self.in_date_range(start_of_day, end_of_day)
161
+
162
+ def this_week(self) -> "EventQueryBuilder":
163
+ """
164
+ Filter to events happening this week (Monday to Sunday).
165
+ Returns:
166
+ Self for method chaining
167
+ """
168
+ today = date.today()
169
+ days_since_monday = today.weekday()
170
+ monday = today - timedelta(days=days_since_monday)
171
+ sunday = monday + timedelta(days=6)
172
+
173
+ start_of_week = date_start(monday)
174
+ end_of_week = date_end(sunday)
175
+ return self.in_date_range(start_of_week, end_of_week)
176
+
177
+ def next_week(self) -> "EventQueryBuilder":
178
+ """
179
+ Filter to events happening next week (Monday to Sunday).
180
+ Returns:
181
+ Self for method chaining
182
+ """
183
+ today = date.today()
184
+ days_since_monday = today.weekday()
185
+ next_monday = today + timedelta(days=(7 - days_since_monday))
186
+ next_sunday = next_monday + timedelta(days=6)
187
+
188
+ start_of_week = date_start(next_monday)
189
+ end_of_week = date_end(next_sunday)
190
+ return self.in_date_range(start_of_week, end_of_week)
191
+
192
+ def this_month(self) -> "EventQueryBuilder":
193
+ """
194
+ Filter to events happening this month.
195
+ Returns:
196
+ Self for method chaining
197
+ """
198
+ today = date.today()
199
+ first_day = date(today.year, today.month, 1)
200
+
201
+ # Calculate last day of month
202
+ if today.month == 12:
203
+ last_day = date(today.year + 1, 1, 1) - timedelta(days=1)
204
+ else:
205
+ last_day = date(today.year, today.month + 1, 1) - timedelta(days=1)
206
+
207
+ start_of_month = date_start(first_day)
208
+ end_of_month = date_end(last_day)
209
+ return self.in_date_range(start_of_month, end_of_month)
210
+
211
+ def next_days(self, days: int) -> "EventQueryBuilder":
212
+ """
213
+ Filter to events happening in the next N days.
214
+ Args:
215
+ days: Number of days from today
216
+ Returns:
217
+ Self for method chaining
218
+ """
219
+ if days < 1:
220
+ raise ValueError("Days must be positive")
221
+
222
+ start = days_from_today(0) # Start of today
223
+ end = days_from_today(days)
224
+ return self.in_date_range(start, end)
225
+
226
+ def last_days(self, days: int) -> "EventQueryBuilder":
227
+ """
228
+ Filter to events that happened in the last N days.
229
+ Args:
230
+ days: Number of days before today
231
+ Returns:
232
+ Self for method chaining
233
+ """
234
+ if days < 1:
235
+ raise ValueError("Days must be positive")
236
+
237
+ end = date_end(date.today())
238
+ start = days_from_today(-days)
239
+ return self.in_date_range(start, end)
240
+
241
+ def _apply_post_filters(self, events: List["CalendarEvent"]) -> List["CalendarEvent"]:
242
+ """
243
+ Apply client-side filters that can't be handled by the API.
244
+ Args:
245
+ events: List of events from API
246
+ Returns:
247
+ Filtered list of events
248
+ """
249
+ filtered = events
250
+
251
+ # Filter by attendee
252
+ if self._attendee_filter:
253
+ filtered = [event for event in filtered if event.has_attendee(self._attendee_filter)]
254
+
255
+ # Filter by location presence
256
+ if self._has_location_filter is not None:
257
+ if self._has_location_filter:
258
+ filtered = [event for event in filtered if event.location]
259
+ else:
260
+ filtered = [event for event in filtered if not event.location]
261
+
262
+ return filtered
263
+
264
+ def execute(self) -> List["CalendarEvent"]:
265
+ """
266
+ Execute the query and return the results.
267
+ Returns:
268
+ List of CalendarEvent objects matching the criteria
269
+ Raises:
270
+ ValueError: If query parameters are invalid
271
+ """
272
+
273
+ # Use the service layer implementation
274
+ events = self._api_service.list_events(
275
+ max_results=self._max_results,
276
+ start=self._start,
277
+ end=self._end,
278
+ query=self._query,
279
+ calendar_id=self._calendar_id,
280
+ single_events=self._single_events_only
281
+ )
282
+
283
+ # Apply any client-side filters
284
+ filtered_events = self._apply_post_filters(events)
285
+
286
+ return filtered_events
287
+
288
+ def count(self) -> int:
289
+ """
290
+ Execute the query and return only the count of matching events.
291
+ Returns:
292
+ Number of events matching the criteria
293
+ """
294
+ return len(self.execute())
295
+
296
+ def first(self) -> Optional["CalendarEvent"]:
297
+ """
298
+ Execute the query and return only the first matching event.
299
+ Returns:
300
+ First CalendarEvent or None if no matches
301
+ """
302
+ events = self.limit(1).execute()
303
+ return events[0] if events else None
304
+
305
+ def exists(self) -> bool:
306
+ """
307
+ Check if any events match the criteria without retrieving them.
308
+ Returns:
309
+ True if at least one event matches, False otherwise
310
+ """
311
+ return self.limit(1).count() > 0
312
+
313
+ def __repr__(self):
314
+ return f"EventQueryBuilder(query='{self._query}', limit={self._max_results}, calendar_id='{self._calendar_id}')"