google-api-client-wrapper 1.1.3__tar.gz → 1.1.4__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.
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/PKG-INFO +1 -1
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_api_client_wrapper.egg-info/PKG-INFO +1 -1
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/calendar/utils.py +69 -68
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/pyproject.toml +1 -1
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/LICENSE +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/README.md +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_api_client_wrapper.egg-info/SOURCES.txt +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_api_client_wrapper.egg-info/dependency_links.txt +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_api_client_wrapper.egg-info/requires.txt +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_api_client_wrapper.egg-info/top_level.txt +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/__init__.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/__init__.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/calendar/__init__.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/calendar/api_service.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/calendar/constants.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/calendar/exceptions.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/calendar/query_builder.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/calendar/types.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/drive/__init__.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/drive/api_service.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/drive/constants.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/drive/exceptions.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/drive/query_builder.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/drive/types.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/drive/utils.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/gmail/__init__.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/gmail/api_service.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/gmail/constants.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/gmail/exceptions.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/gmail/query_builder.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/gmail/types.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/gmail/utils.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/tasks/__init__.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/tasks/api_service.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/tasks/constants.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/tasks/exceptions.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/tasks/query_builder.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/tasks/types.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/services/tasks/utils.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/user_client.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/utils/__init__.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/utils/datetime.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/utils/validation.py +0 -0
- {google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-api-client-wrapper
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.4
|
|
4
4
|
Summary: A comprehensive Python wrapper for Google APIs, providing clean and intuitive access to Gmail, Google Drive, Google Calendar, and Google Tasks services.
|
|
5
5
|
Author-email: Dagmawi Molla <dagmawishewadeg@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-api-client-wrapper
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.4
|
|
4
4
|
Summary: A comprehensive Python wrapper for Google APIs, providing clean and intuitive access to Gmail, Google Drive, Google Calendar, and Google Tasks services.
|
|
5
5
|
Author-email: Dagmawi Molla <dagmawishewadeg@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Optional, List, Dict, Any
|
|
3
3
|
|
|
4
|
+
import tzlocal
|
|
5
|
+
|
|
4
6
|
from .types import CalendarEvent, Attendee, TimeSlot, FreeBusyResponse
|
|
5
7
|
from .constants import (
|
|
6
8
|
MAX_SUMMARY_LENGTH, MAX_DESCRIPTION_LENGTH, MAX_LOCATION_LENGTH,
|
|
@@ -8,7 +10,6 @@ from .constants import (
|
|
|
8
10
|
)
|
|
9
11
|
from ...utils.datetime import convert_datetime_to_iso
|
|
10
12
|
|
|
11
|
-
|
|
12
13
|
# Import from shared utilities
|
|
13
14
|
from ...utils.validation import is_valid_email, validate_text_field, sanitize_header_value
|
|
14
15
|
|
|
@@ -22,16 +23,16 @@ def validate_datetime_range(start: Optional[datetime], end: Optional[datetime])
|
|
|
22
23
|
def parse_datetime_from_api(datetime_data: Dict[str, Any]) -> Optional[datetime]:
|
|
23
24
|
"""
|
|
24
25
|
Parse datetime from Google Calendar API response.
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
Args:
|
|
27
28
|
datetime_data: Dictionary containing dateTime or date fields
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
Returns:
|
|
30
31
|
Parsed datetime object or None if parsing fails
|
|
31
32
|
"""
|
|
32
33
|
if not datetime_data:
|
|
33
34
|
return None
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
try:
|
|
36
37
|
if datetime_data.get("dateTime"):
|
|
37
38
|
# Handle timezone-aware datetime
|
|
@@ -44,22 +45,22 @@ def parse_datetime_from_api(datetime_data: Dict[str, Any]) -> Optional[datetime]
|
|
|
44
45
|
return datetime.strptime(datetime_data["date"], "%Y-%m-%d")
|
|
45
46
|
except (ValueError, TypeError):
|
|
46
47
|
pass
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
return None
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
def parse_attendees_from_api(attendees_data: List[Dict[str, Any]]) -> List[Attendee]:
|
|
52
53
|
"""
|
|
53
54
|
Parse attendees from Google Calendar API response.
|
|
54
|
-
|
|
55
|
+
|
|
55
56
|
Args:
|
|
56
57
|
attendees_data: List of attendee dictionaries from API
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
Returns:
|
|
59
60
|
List of Attendee objects
|
|
60
61
|
"""
|
|
61
62
|
attendees = []
|
|
62
|
-
|
|
63
|
+
|
|
63
64
|
for attendee_data in attendees_data:
|
|
64
65
|
email = attendee_data.get("email")
|
|
65
66
|
if email and is_valid_email(email):
|
|
@@ -67,7 +68,7 @@ def parse_attendees_from_api(attendees_data: List[Dict[str, Any]]) -> List[Atten
|
|
|
67
68
|
response_status = attendee_data.get("responseStatus")
|
|
68
69
|
if response_status and response_status not in VALID_RESPONSE_STATUSES:
|
|
69
70
|
response_status = None
|
|
70
|
-
|
|
71
|
+
|
|
71
72
|
attendees.append(Attendee(
|
|
72
73
|
email=email,
|
|
73
74
|
display_name=attendee_data.get("displayName"),
|
|
@@ -75,17 +76,17 @@ def parse_attendees_from_api(attendees_data: List[Dict[str, Any]]) -> List[Atten
|
|
|
75
76
|
))
|
|
76
77
|
except ValueError:
|
|
77
78
|
pass
|
|
78
|
-
|
|
79
|
+
|
|
79
80
|
return attendees
|
|
80
81
|
|
|
81
82
|
|
|
82
83
|
def from_google_event(google_event: Dict[str, Any]) -> CalendarEvent:
|
|
83
84
|
"""
|
|
84
85
|
Create a CalendarEvent instance from a Google Calendar API response.
|
|
85
|
-
|
|
86
|
+
|
|
86
87
|
Args:
|
|
87
88
|
google_event: Dictionary containing event data from Google Calendar API
|
|
88
|
-
|
|
89
|
+
|
|
89
90
|
Returns:
|
|
90
91
|
CalendarEvent instance populated with the data from the dictionary
|
|
91
92
|
"""
|
|
@@ -96,31 +97,31 @@ def from_google_event(google_event: Dict[str, Any]) -> CalendarEvent:
|
|
|
96
97
|
description = google_event.get("description", "").strip() if google_event.get("description") else None
|
|
97
98
|
location = google_event.get("location", "").strip() if google_event.get("location") else None
|
|
98
99
|
html_link = google_event.get("htmlLink")
|
|
99
|
-
|
|
100
|
+
|
|
100
101
|
# Parse datetimes
|
|
101
102
|
start = parse_datetime_from_api(google_event.get("start", {}))
|
|
102
103
|
end = parse_datetime_from_api(google_event.get("end", {}))
|
|
103
|
-
|
|
104
|
+
|
|
104
105
|
# Parse attendees
|
|
105
106
|
attendees_data = google_event.get("attendees", [])
|
|
106
107
|
attendees = parse_attendees_from_api(attendees_data)
|
|
107
|
-
|
|
108
|
+
|
|
108
109
|
# Parse recurrence
|
|
109
110
|
recurrence = google_event.get("recurrence", [])
|
|
110
111
|
recurring_event_id = google_event.get("recurringEventId")
|
|
111
|
-
|
|
112
|
+
|
|
112
113
|
# Parse creator and organizer
|
|
113
114
|
creator_data = google_event.get("creator", {})
|
|
114
115
|
creator = creator_data.get("email") if creator_data else None
|
|
115
|
-
|
|
116
|
+
|
|
116
117
|
organizer_data = google_event.get("organizer", {})
|
|
117
118
|
organizer = organizer_data.get("email") if organizer_data else None
|
|
118
|
-
|
|
119
|
+
|
|
119
120
|
# Parse status
|
|
120
121
|
status = google_event.get("status", "confirmed")
|
|
121
122
|
if status not in VALID_EVENT_STATUSES:
|
|
122
123
|
status = "confirmed"
|
|
123
|
-
|
|
124
|
+
|
|
124
125
|
# Create and return the event
|
|
125
126
|
event = CalendarEvent(
|
|
126
127
|
event_id=event_id,
|
|
@@ -137,37 +138,37 @@ def from_google_event(google_event: Dict[str, Any]) -> CalendarEvent:
|
|
|
137
138
|
organizer=organizer,
|
|
138
139
|
status=status
|
|
139
140
|
)
|
|
140
|
-
|
|
141
|
+
|
|
141
142
|
return event
|
|
142
|
-
|
|
143
|
+
|
|
143
144
|
except Exception:
|
|
144
145
|
raise ValueError("Invalid event data - failed to parse calendar event")
|
|
145
146
|
|
|
146
147
|
|
|
147
148
|
def create_event_body(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
start: datetime,
|
|
150
|
+
end: datetime,
|
|
151
|
+
summary: str = None,
|
|
152
|
+
description: str = None,
|
|
153
|
+
location: str = None,
|
|
154
|
+
attendees: List[Attendee] = None,
|
|
155
|
+
recurrence: List[str] = None
|
|
155
156
|
) -> Dict[str, Any]:
|
|
156
157
|
"""
|
|
157
158
|
Create event body dictionary for Google Calendar API.
|
|
158
|
-
|
|
159
|
+
|
|
159
160
|
Args:
|
|
160
161
|
start: Event start datetime
|
|
161
|
-
end: Event end datetime
|
|
162
|
+
end: Event end datetime
|
|
162
163
|
summary: Event summary/title
|
|
163
164
|
description: Event description
|
|
164
165
|
location: Event location
|
|
165
166
|
attendees: List of attendees
|
|
166
167
|
recurrence: List of recurrence rules
|
|
167
|
-
|
|
168
|
+
|
|
168
169
|
Returns:
|
|
169
170
|
Dictionary suitable for Calendar API requests
|
|
170
|
-
|
|
171
|
+
|
|
171
172
|
Raises:
|
|
172
173
|
ValueError: If required fields are invalid
|
|
173
174
|
"""
|
|
@@ -175,19 +176,19 @@ def create_event_body(
|
|
|
175
176
|
raise ValueError("Event must have both start and end times")
|
|
176
177
|
if start >= end:
|
|
177
178
|
raise ValueError("Event start time must be before end time")
|
|
178
|
-
|
|
179
|
+
|
|
179
180
|
# Validate text fields
|
|
180
181
|
validate_text_field(summary, MAX_SUMMARY_LENGTH, "summary")
|
|
181
182
|
validate_text_field(description, MAX_DESCRIPTION_LENGTH, "description")
|
|
182
183
|
validate_text_field(location, MAX_LOCATION_LENGTH, "location")
|
|
183
|
-
|
|
184
|
+
|
|
184
185
|
# Build event body
|
|
185
186
|
event_body = {
|
|
186
187
|
'summary': summary or "New Event",
|
|
187
|
-
'start': {'dateTime': convert_datetime_to_iso(start)},
|
|
188
|
-
'end': {'dateTime': convert_datetime_to_iso(end)}
|
|
188
|
+
'start': {'dateTime': convert_datetime_to_iso(start), 'timeZone': tzlocal.get_localzone_name()},
|
|
189
|
+
'end': {'dateTime': convert_datetime_to_iso(end), 'timeZone': tzlocal.get_localzone_name()}
|
|
189
190
|
}
|
|
190
|
-
|
|
191
|
+
|
|
191
192
|
# Add optional fields
|
|
192
193
|
if description:
|
|
193
194
|
event_body['description'] = sanitize_header_value(description)
|
|
@@ -197,50 +198,50 @@ def create_event_body(
|
|
|
197
198
|
event_body['attendees'] = [attendee.to_dict() for attendee in attendees]
|
|
198
199
|
if recurrence:
|
|
199
200
|
event_body['recurrence'] = recurrence
|
|
200
|
-
|
|
201
|
+
|
|
201
202
|
return event_body
|
|
202
203
|
|
|
203
204
|
|
|
204
205
|
def parse_freebusy_response(freebusy_data: Dict[str, Any]) -> FreeBusyResponse:
|
|
205
206
|
"""
|
|
206
207
|
Parse a freebusy response from Google Calendar API.
|
|
207
|
-
|
|
208
|
+
|
|
208
209
|
Args:
|
|
209
210
|
freebusy_data: Dictionary containing freebusy response from API
|
|
210
|
-
|
|
211
|
+
|
|
211
212
|
Returns:
|
|
212
213
|
FreeBusyResponse object with parsed data
|
|
213
|
-
|
|
214
|
+
|
|
214
215
|
Raises:
|
|
215
216
|
ValueError: If the response data is invalid
|
|
216
217
|
"""
|
|
217
218
|
if not freebusy_data:
|
|
218
219
|
raise ValueError("Empty freebusy response data")
|
|
219
|
-
|
|
220
|
+
|
|
220
221
|
try:
|
|
221
222
|
# Parse time range
|
|
222
223
|
time_min = freebusy_data.get("timeMin")
|
|
223
224
|
time_max = freebusy_data.get("timeMax")
|
|
224
|
-
|
|
225
|
+
|
|
225
226
|
if not time_min or not time_max:
|
|
226
227
|
raise ValueError("Missing timeMin or timeMax in freebusy response")
|
|
227
|
-
|
|
228
|
+
|
|
228
229
|
# Parse start and end times
|
|
229
230
|
start = datetime.fromisoformat(time_min.replace('Z', '+00:00'))
|
|
230
231
|
end = datetime.fromisoformat(time_max.replace('Z', '+00:00'))
|
|
231
|
-
|
|
232
|
+
|
|
232
233
|
# Parse calendar busy periods
|
|
233
234
|
calendars = {}
|
|
234
235
|
calendars_data = freebusy_data.get("calendars", {})
|
|
235
|
-
|
|
236
|
+
|
|
236
237
|
for calendar_id, calendar_data in calendars_data.items():
|
|
237
238
|
busy_periods = []
|
|
238
239
|
busy_data = calendar_data.get("busy", [])
|
|
239
|
-
|
|
240
|
+
|
|
240
241
|
for busy_period in busy_data:
|
|
241
242
|
period_start_str = busy_period.get("start")
|
|
242
243
|
period_end_str = busy_period.get("end")
|
|
243
|
-
|
|
244
|
+
|
|
244
245
|
if period_start_str and period_end_str:
|
|
245
246
|
try:
|
|
246
247
|
period_start = datetime.fromisoformat(period_start_str.replace('Z', '+00:00'))
|
|
@@ -248,27 +249,27 @@ def parse_freebusy_response(freebusy_data: Dict[str, Any]) -> FreeBusyResponse:
|
|
|
248
249
|
busy_periods.append(TimeSlot(start=period_start, end=period_end))
|
|
249
250
|
except (ValueError, TypeError):
|
|
250
251
|
continue
|
|
251
|
-
|
|
252
|
+
|
|
252
253
|
calendars[calendar_id] = busy_periods
|
|
253
|
-
|
|
254
|
+
|
|
254
255
|
# Parse errors
|
|
255
256
|
errors = {}
|
|
256
257
|
errors_data = freebusy_data.get("errors", {})
|
|
257
|
-
|
|
258
|
+
|
|
258
259
|
for calendar_id, error_data in errors_data.items():
|
|
259
260
|
if isinstance(error_data, list) and error_data:
|
|
260
261
|
error_reason = error_data[0].get("reason", "Unknown error")
|
|
261
262
|
errors[calendar_id] = error_reason
|
|
262
263
|
elif isinstance(error_data, str):
|
|
263
264
|
errors[calendar_id] = error_data
|
|
264
|
-
|
|
265
|
+
|
|
265
266
|
return FreeBusyResponse(
|
|
266
267
|
start=start,
|
|
267
268
|
end=end,
|
|
268
269
|
calendars=calendars,
|
|
269
270
|
errors=errors
|
|
270
271
|
)
|
|
271
|
-
|
|
272
|
+
|
|
272
273
|
except Exception as e:
|
|
273
274
|
raise ValueError(f"Failed to parse freebusy response: {str(e)}")
|
|
274
275
|
|
|
@@ -276,23 +277,23 @@ def parse_freebusy_response(freebusy_data: Dict[str, Any]) -> FreeBusyResponse:
|
|
|
276
277
|
def merge_overlapping_time_slots(time_slots: List[TimeSlot]) -> List[TimeSlot]:
|
|
277
278
|
"""
|
|
278
279
|
Merge overlapping time slots into consolidated periods.
|
|
279
|
-
|
|
280
|
+
|
|
280
281
|
Args:
|
|
281
282
|
time_slots: List of TimeSlot objects that may overlap
|
|
282
|
-
|
|
283
|
+
|
|
283
284
|
Returns:
|
|
284
285
|
List of merged TimeSlot objects with no overlaps
|
|
285
286
|
"""
|
|
286
287
|
if not time_slots:
|
|
287
288
|
return []
|
|
288
|
-
|
|
289
|
+
|
|
289
290
|
# Sort by start time
|
|
290
291
|
sorted_slots = sorted(time_slots, key=lambda x: x.start)
|
|
291
292
|
merged = [sorted_slots[0]]
|
|
292
|
-
|
|
293
|
+
|
|
293
294
|
for current in sorted_slots[1:]:
|
|
294
295
|
last_merged = merged[-1]
|
|
295
|
-
|
|
296
|
+
|
|
296
297
|
# Check if current slot overlaps with the last merged slot
|
|
297
298
|
if current.start <= last_merged.end:
|
|
298
299
|
# Merge by extending the end time if necessary
|
|
@@ -301,38 +302,38 @@ def merge_overlapping_time_slots(time_slots: List[TimeSlot]) -> List[TimeSlot]:
|
|
|
301
302
|
else:
|
|
302
303
|
# No overlap, add as new slot
|
|
303
304
|
merged.append(current)
|
|
304
|
-
|
|
305
|
+
|
|
305
306
|
return merged
|
|
306
307
|
|
|
307
308
|
|
|
308
309
|
def validate_freebusy_request(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
310
|
+
start: datetime,
|
|
311
|
+
end: datetime,
|
|
312
|
+
calendar_ids: List[str]
|
|
312
313
|
) -> None:
|
|
313
314
|
"""
|
|
314
315
|
Validate parameters for a freebusy request.
|
|
315
|
-
|
|
316
|
+
|
|
316
317
|
Args:
|
|
317
318
|
start: Start datetime for the query
|
|
318
319
|
end: End datetime for the query
|
|
319
320
|
calendar_ids: List of calendar IDs to query
|
|
320
|
-
|
|
321
|
+
|
|
321
322
|
Raises:
|
|
322
323
|
ValueError: If any parameter is invalid
|
|
323
324
|
"""
|
|
324
325
|
from .constants import MAX_FREEBUSY_DAYS_RANGE, MAX_CALENDARS_PER_FREEBUSY_QUERY
|
|
325
|
-
|
|
326
|
+
|
|
326
327
|
if start >= end:
|
|
327
328
|
raise ValueError("Start time must be before end time")
|
|
328
|
-
|
|
329
|
+
|
|
329
330
|
# Check maximum time range (Google's API limit)
|
|
330
331
|
days_diff = (end - start).days
|
|
331
332
|
if days_diff > MAX_FREEBUSY_DAYS_RANGE:
|
|
332
333
|
raise ValueError(f"Time range cannot exceed {MAX_FREEBUSY_DAYS_RANGE} days")
|
|
333
|
-
|
|
334
|
+
|
|
334
335
|
if not calendar_ids:
|
|
335
336
|
raise ValueError("At least one calendar ID must be specified")
|
|
336
|
-
|
|
337
|
+
|
|
337
338
|
if len(calendar_ids) > MAX_CALENDARS_PER_FREEBUSY_QUERY:
|
|
338
339
|
raise ValueError(f"Cannot query more than {MAX_CALENDARS_PER_FREEBUSY_QUERY} calendars at once")
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "google-api-client-wrapper"
|
|
9
|
-
version = "1.1.
|
|
9
|
+
version = "1.1.4"
|
|
10
10
|
description = "A comprehensive Python wrapper for Google APIs, providing clean and intuitive access to Gmail, Google Drive, Google Calendar, and Google Tasks services."
|
|
11
11
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
12
12
|
authors = [{name = "Dagmawi Molla", email = "dagmawishewadeg@gmail.com"}]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/user_client.py
RENAMED
|
File without changes
|
{google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/utils/__init__.py
RENAMED
|
File without changes
|
{google_api_client_wrapper-1.1.3 → google_api_client_wrapper-1.1.4}/google_client/utils/datetime.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|