universal-mcp-applications 0.1.30rc1__py3-none-any.whl → 0.1.36rc1__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.
Potentially problematic release.
This version of universal-mcp-applications might be problematic. Click here for more details.
- universal_mcp/applications/ahrefs/app.py +52 -198
- universal_mcp/applications/airtable/app.py +23 -122
- universal_mcp/applications/apollo/app.py +111 -464
- universal_mcp/applications/asana/app.py +417 -1567
- universal_mcp/applications/aws_s3/app.py +33 -100
- universal_mcp/applications/bill/app.py +546 -1957
- universal_mcp/applications/box/app.py +1068 -3981
- universal_mcp/applications/braze/app.py +364 -1430
- universal_mcp/applications/browser_use/app.py +2 -8
- universal_mcp/applications/cal_com_v2/app.py +207 -625
- universal_mcp/applications/calendly/app.py +61 -200
- universal_mcp/applications/canva/app.py +45 -110
- universal_mcp/applications/clickup/app.py +207 -674
- universal_mcp/applications/coda/app.py +146 -426
- universal_mcp/applications/confluence/app.py +310 -1098
- universal_mcp/applications/contentful/app.py +36 -151
- universal_mcp/applications/crustdata/app.py +28 -107
- universal_mcp/applications/dialpad/app.py +283 -756
- universal_mcp/applications/digitalocean/app.py +1766 -5777
- universal_mcp/applications/domain_checker/app.py +3 -54
- universal_mcp/applications/e2b/app.py +14 -64
- universal_mcp/applications/elevenlabs/app.py +9 -47
- universal_mcp/applications/exa/app.py +6 -17
- universal_mcp/applications/falai/app.py +23 -100
- universal_mcp/applications/figma/app.py +53 -137
- universal_mcp/applications/file_system/app.py +2 -13
- universal_mcp/applications/firecrawl/app.py +51 -152
- universal_mcp/applications/fireflies/app.py +59 -281
- universal_mcp/applications/fpl/app.py +91 -528
- universal_mcp/applications/fpl/utils/fixtures.py +15 -49
- universal_mcp/applications/fpl/utils/helper.py +25 -89
- universal_mcp/applications/fpl/utils/league_utils.py +20 -64
- universal_mcp/applications/ghost_content/app.py +52 -161
- universal_mcp/applications/github/app.py +19 -56
- universal_mcp/applications/gong/app.py +88 -248
- universal_mcp/applications/google_calendar/app.py +16 -68
- universal_mcp/applications/google_docs/app.py +88 -188
- universal_mcp/applications/google_drive/app.py +140 -462
- universal_mcp/applications/google_gemini/app.py +12 -64
- universal_mcp/applications/google_mail/app.py +28 -157
- universal_mcp/applications/google_searchconsole/app.py +15 -48
- universal_mcp/applications/google_sheet/app.py +101 -578
- universal_mcp/applications/google_sheet/helper.py +10 -37
- universal_mcp/applications/hashnode/app.py +57 -269
- universal_mcp/applications/heygen/app.py +44 -122
- universal_mcp/applications/http_tools/app.py +10 -32
- universal_mcp/applications/hubspot/api_segments/crm_api.py +460 -1573
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +74 -262
- universal_mcp/applications/hubspot/app.py +23 -87
- universal_mcp/applications/jira/app.py +2071 -7986
- universal_mcp/applications/klaviyo/app.py +494 -1376
- universal_mcp/applications/linkedin/README.md +23 -4
- universal_mcp/applications/linkedin/app.py +392 -212
- universal_mcp/applications/mailchimp/app.py +450 -1605
- universal_mcp/applications/markitdown/app.py +8 -20
- universal_mcp/applications/miro/app.py +217 -699
- universal_mcp/applications/ms_teams/app.py +64 -186
- universal_mcp/applications/neon/app.py +86 -192
- universal_mcp/applications/notion/app.py +21 -36
- universal_mcp/applications/onedrive/app.py +14 -36
- universal_mcp/applications/openai/app.py +42 -165
- universal_mcp/applications/outlook/app.py +16 -76
- universal_mcp/applications/perplexity/app.py +4 -19
- universal_mcp/applications/pipedrive/app.py +832 -3142
- universal_mcp/applications/posthog/app.py +163 -432
- universal_mcp/applications/reddit/app.py +40 -139
- universal_mcp/applications/resend/app.py +41 -107
- universal_mcp/applications/retell/app.py +14 -41
- universal_mcp/applications/rocketlane/app.py +221 -934
- universal_mcp/applications/scraper/README.md +7 -4
- universal_mcp/applications/scraper/app.py +280 -93
- universal_mcp/applications/semanticscholar/app.py +22 -64
- universal_mcp/applications/semrush/app.py +43 -77
- universal_mcp/applications/sendgrid/app.py +512 -1262
- universal_mcp/applications/sentry/app.py +271 -906
- universal_mcp/applications/serpapi/app.py +40 -143
- universal_mcp/applications/sharepoint/app.py +15 -37
- universal_mcp/applications/shopify/app.py +1551 -4287
- universal_mcp/applications/shortcut/app.py +155 -417
- universal_mcp/applications/slack/app.py +50 -101
- universal_mcp/applications/spotify/app.py +126 -325
- universal_mcp/applications/supabase/app.py +104 -213
- universal_mcp/applications/tavily/app.py +1 -1
- universal_mcp/applications/trello/app.py +693 -2656
- universal_mcp/applications/twilio/app.py +14 -50
- universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
- universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
- universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
- universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
- universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
- universal_mcp/applications/whatsapp/app.py +35 -186
- universal_mcp/applications/whatsapp/audio.py +2 -6
- universal_mcp/applications/whatsapp/whatsapp.py +17 -51
- universal_mcp/applications/whatsapp_business/app.py +70 -283
- universal_mcp/applications/wrike/app.py +45 -118
- universal_mcp/applications/yahoo_finance/app.py +19 -65
- universal_mcp/applications/youtube/app.py +75 -261
- universal_mcp/applications/zenquotes/app.py +2 -2
- {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/METADATA +2 -2
- {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/RECORD +105 -106
- universal_mcp/applications/scraper/scraper_testers.py +0 -17
- {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.30rc1.dist-info → universal_mcp_applications-0.1.36rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
|
-
|
|
3
2
|
from universal_mcp.applications.application import APIApplication
|
|
4
3
|
from universal_mcp.integrations import Integration
|
|
5
|
-
|
|
6
|
-
from universal_mcp.applications.google_sheet.helper import (
|
|
7
|
-
analyze_sheet_for_tables,
|
|
8
|
-
analyze_table_schema,
|
|
9
|
-
)
|
|
4
|
+
from universal_mcp.applications.google_sheet.helper import analyze_sheet_for_tables, analyze_table_schema
|
|
10
5
|
|
|
11
6
|
|
|
12
7
|
class GoogleSheetApp(APIApplication):
|
|
@@ -19,9 +14,9 @@ class GoogleSheetApp(APIApplication):
|
|
|
19
14
|
super().__init__(name="google_sheet", integration=integration)
|
|
20
15
|
self.base_url = "https://sheets.googleapis.com/v4/spreadsheets"
|
|
21
16
|
|
|
22
|
-
def create_spreadsheet(self, title: str) -> dict[str, Any]:
|
|
17
|
+
async def create_spreadsheet(self, title: str) -> dict[str, Any]:
|
|
23
18
|
"""
|
|
24
|
-
Creates a new, blank Google Spreadsheet file with a specified title. This function generates a completely new document, unlike `add_sheet` which adds a worksheet (tab) to an existing spreadsheet. It returns the API response containing the new spreadsheet's metadata.
|
|
19
|
+
Creates a new, blank Google Spreadsheet file with a specified title. This function generates a completely new document, unlike `add_sheet` which adds a worksheet (tab) to an existing spreadsheet. It returns the API response containing the new spreadsheet's metadata. Note that you need to call other google_sheet functions (e.g. `google_sheet__write_values_to_sheet`) to actually add content after creating the spreadsheet.
|
|
25
20
|
|
|
26
21
|
Args:
|
|
27
22
|
title: String representing the desired title for the new spreadsheet
|
|
@@ -39,9 +34,11 @@ class GoogleSheetApp(APIApplication):
|
|
|
39
34
|
url = self.base_url
|
|
40
35
|
spreadsheet_data = {"properties": {"title": title}}
|
|
41
36
|
response = self._post(url, data=spreadsheet_data)
|
|
42
|
-
|
|
37
|
+
payload = self._handle_response(response)
|
|
38
|
+
payload["Note"] = "You must load and call other google_sheet content functions (like `google_sheet__write_values_to_sheet`)"
|
|
39
|
+
return payload
|
|
43
40
|
|
|
44
|
-
def get_spreadsheet_metadata(self, spreadsheetId: str) -> dict[str, Any]:
|
|
41
|
+
async def get_spreadsheet_metadata(self, spreadsheetId: str) -> dict[str, Any]:
|
|
45
42
|
"""
|
|
46
43
|
Retrieves a spreadsheet's metadata and structural properties, such as sheet names, IDs, and named ranges, using its unique ID. This function intentionally excludes cell data, distinguishing it from `get_values` which fetches the actual content within cells.
|
|
47
44
|
|
|
@@ -63,7 +60,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
63
60
|
response = self._get(url)
|
|
64
61
|
return self._handle_response(response)
|
|
65
62
|
|
|
66
|
-
def get_values(
|
|
63
|
+
async def get_values(
|
|
67
64
|
self,
|
|
68
65
|
spreadsheetId: str,
|
|
69
66
|
range: str,
|
|
@@ -93,20 +90,16 @@ class GoogleSheetApp(APIApplication):
|
|
|
93
90
|
"""
|
|
94
91
|
url = f"{self.base_url}/{spreadsheetId}/values/{range}"
|
|
95
92
|
params = {}
|
|
96
|
-
|
|
97
93
|
if majorDimension:
|
|
98
94
|
params["majorDimension"] = majorDimension
|
|
99
95
|
if valueRenderOption:
|
|
100
96
|
params["valueRenderOption"] = valueRenderOption
|
|
101
97
|
if dateTimeRenderOption:
|
|
102
98
|
params["dateTimeRenderOption"] = dateTimeRenderOption
|
|
103
|
-
|
|
104
99
|
response = self._get(url, params=params)
|
|
105
100
|
return self._handle_response(response)
|
|
106
101
|
|
|
107
|
-
def batch_get_values_by_range(
|
|
108
|
-
self, spreadsheetId: str, ranges: list[str] | None = None
|
|
109
|
-
) -> dict[str, Any]:
|
|
102
|
+
async def batch_get_values_by_range(self, spreadsheetId: str, ranges: list[str] | None = None) -> dict[str, Any]:
|
|
110
103
|
"""
|
|
111
104
|
Efficiently retrieves values from multiple predefined A1 notation ranges in a single API request. Unlike `get_values`, which fetches a single range, or `batch_get_values_by_data_filter`, which uses dynamic filtering criteria, this function operates on a simple list of range strings for bulk data retrieval.
|
|
112
105
|
|
|
@@ -131,7 +124,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
131
124
|
response = self._get(url, params=params)
|
|
132
125
|
return self._handle_response(response)
|
|
133
126
|
|
|
134
|
-
def insert_dimensions(
|
|
127
|
+
async def insert_dimensions(
|
|
135
128
|
self,
|
|
136
129
|
spreadsheetId: str,
|
|
137
130
|
sheet_id: int,
|
|
@@ -172,56 +165,33 @@ class GoogleSheetApp(APIApplication):
|
|
|
172
165
|
"""
|
|
173
166
|
if not spreadsheetId:
|
|
174
167
|
raise ValueError("spreadsheetId cannot be empty")
|
|
175
|
-
|
|
176
168
|
if dimension not in ["ROWS", "COLUMNS"]:
|
|
177
169
|
raise ValueError('dimension must be either "ROWS" or "COLUMNS"')
|
|
178
|
-
|
|
179
170
|
if start_index < 0 or end_index < 0:
|
|
180
171
|
raise ValueError("start_index and end_index must be non-negative")
|
|
181
|
-
|
|
182
172
|
if start_index >= end_index:
|
|
183
173
|
raise ValueError("end_index must be greater than start_index")
|
|
184
|
-
|
|
185
174
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
186
|
-
|
|
187
175
|
request_body: dict[str, Any] = {
|
|
188
176
|
"requests": [
|
|
189
177
|
{
|
|
190
178
|
"insertDimension": {
|
|
191
179
|
"inheritFromBefore": inherit_from_before,
|
|
192
|
-
"range": {
|
|
193
|
-
"dimension": dimension,
|
|
194
|
-
"sheetId": sheet_id,
|
|
195
|
-
"startIndex": start_index,
|
|
196
|
-
"endIndex": end_index,
|
|
197
|
-
},
|
|
180
|
+
"range": {"dimension": dimension, "sheetId": sheet_id, "startIndex": start_index, "endIndex": end_index},
|
|
198
181
|
}
|
|
199
182
|
}
|
|
200
183
|
]
|
|
201
184
|
}
|
|
202
|
-
|
|
203
|
-
# Add optional parameters if provided
|
|
204
185
|
if include_spreadsheet_in_response is not None:
|
|
205
|
-
request_body["includeSpreadsheetInResponse"] =
|
|
206
|
-
include_spreadsheet_in_response
|
|
207
|
-
)
|
|
208
|
-
|
|
186
|
+
request_body["includeSpreadsheetInResponse"] = include_spreadsheet_in_response
|
|
209
187
|
if response_include_grid_data is not None:
|
|
210
188
|
request_body["responseIncludeGridData"] = response_include_grid_data
|
|
211
|
-
|
|
212
189
|
if response_ranges is not None:
|
|
213
190
|
request_body["responseRanges"] = response_ranges
|
|
214
|
-
|
|
215
191
|
response = self._post(url, data=request_body)
|
|
216
192
|
return self._handle_response(response)
|
|
217
193
|
|
|
218
|
-
def append_dimensions(
|
|
219
|
-
self,
|
|
220
|
-
spreadsheetId: str,
|
|
221
|
-
sheet_id: int,
|
|
222
|
-
dimension: str,
|
|
223
|
-
length: int,
|
|
224
|
-
) -> dict[str, Any]:
|
|
194
|
+
async def append_dimensions(self, spreadsheetId: str, sheet_id: int, dimension: str, length: int) -> dict[str, Any]:
|
|
225
195
|
"""
|
|
226
196
|
Adds a specified number of empty rows or columns to the end of a designated sheet. Unlike `insert_dimensions`, which adds space at a specific index, this function exclusively extends the sheet's boundaries at the bottom or to the right without affecting existing content.
|
|
227
197
|
|
|
@@ -246,31 +216,16 @@ class GoogleSheetApp(APIApplication):
|
|
|
246
216
|
"""
|
|
247
217
|
if not spreadsheetId:
|
|
248
218
|
raise ValueError("spreadsheetId cannot be empty")
|
|
249
|
-
|
|
250
219
|
if dimension not in ["ROWS", "COLUMNS"]:
|
|
251
220
|
raise ValueError('dimension must be either "ROWS" or "COLUMNS"')
|
|
252
|
-
|
|
253
221
|
if length <= 0:
|
|
254
222
|
raise ValueError("length must be a positive integer")
|
|
255
|
-
|
|
256
223
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
257
|
-
|
|
258
|
-
request_body = {
|
|
259
|
-
"requests": [
|
|
260
|
-
{
|
|
261
|
-
"appendDimension": {
|
|
262
|
-
"sheetId": sheet_id,
|
|
263
|
-
"dimension": dimension,
|
|
264
|
-
"length": length,
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
]
|
|
268
|
-
}
|
|
269
|
-
|
|
224
|
+
request_body = {"requests": [{"appendDimension": {"sheetId": sheet_id, "dimension": dimension, "length": length}}]}
|
|
270
225
|
response = self._post(url, data=request_body)
|
|
271
226
|
return self._handle_response(response)
|
|
272
227
|
|
|
273
|
-
def delete_dimensions(
|
|
228
|
+
async def delete_dimensions(
|
|
274
229
|
self,
|
|
275
230
|
spreadsheetId: str,
|
|
276
231
|
sheet_id: int,
|
|
@@ -306,49 +261,32 @@ class GoogleSheetApp(APIApplication):
|
|
|
306
261
|
"""
|
|
307
262
|
if not spreadsheetId:
|
|
308
263
|
raise ValueError("spreadsheetId cannot be empty")
|
|
309
|
-
|
|
310
264
|
if dimension not in ["ROWS", "COLUMNS"]:
|
|
311
265
|
raise ValueError('dimension must be either "ROWS" or "COLUMNS"')
|
|
312
|
-
|
|
313
266
|
if start_index < 0 or end_index < 0:
|
|
314
267
|
raise ValueError("start_index and end_index must be non-negative")
|
|
315
|
-
|
|
316
268
|
if start_index >= end_index:
|
|
317
269
|
raise ValueError("end_index must be greater than start_index")
|
|
318
|
-
|
|
319
270
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
320
|
-
|
|
321
271
|
request_body: dict[str, Any] = {
|
|
322
272
|
"requests": [
|
|
323
273
|
{
|
|
324
274
|
"deleteDimension": {
|
|
325
|
-
"range": {
|
|
326
|
-
"sheetId": sheet_id,
|
|
327
|
-
"dimension": dimension,
|
|
328
|
-
"startIndex": start_index,
|
|
329
|
-
"endIndex": end_index,
|
|
330
|
-
}
|
|
275
|
+
"range": {"sheetId": sheet_id, "dimension": dimension, "startIndex": start_index, "endIndex": end_index}
|
|
331
276
|
}
|
|
332
277
|
}
|
|
333
278
|
]
|
|
334
279
|
}
|
|
335
|
-
|
|
336
|
-
# Add optional response parameters if provided
|
|
337
280
|
if include_spreadsheet_in_response is not None:
|
|
338
|
-
request_body["includeSpreadsheetInResponse"] =
|
|
339
|
-
include_spreadsheet_in_response
|
|
340
|
-
)
|
|
341
|
-
|
|
281
|
+
request_body["includeSpreadsheetInResponse"] = include_spreadsheet_in_response
|
|
342
282
|
if response_include_grid_data is not None:
|
|
343
283
|
request_body["responseIncludeGridData"] = response_include_grid_data
|
|
344
|
-
|
|
345
284
|
if response_ranges is not None:
|
|
346
285
|
request_body["responseRanges"] = response_ranges
|
|
347
|
-
|
|
348
286
|
response = self._post(url, data=request_body)
|
|
349
287
|
return self._handle_response(response)
|
|
350
288
|
|
|
351
|
-
def add_sheet(
|
|
289
|
+
async def add_sheet(
|
|
352
290
|
self,
|
|
353
291
|
spreadsheetId: str,
|
|
354
292
|
title: str | None = None,
|
|
@@ -358,7 +296,6 @@ class GoogleSheetApp(APIApplication):
|
|
|
358
296
|
hidden: bool | None = None,
|
|
359
297
|
rightToLeft: bool | None = None,
|
|
360
298
|
tabColorStyle: dict | None = None,
|
|
361
|
-
# Grid properties
|
|
362
299
|
rowCount: int | None = None,
|
|
363
300
|
columnCount: int | None = None,
|
|
364
301
|
frozenRowCount: int | None = None,
|
|
@@ -366,7 +303,6 @@ class GoogleSheetApp(APIApplication):
|
|
|
366
303
|
hideGridlines: bool | None = None,
|
|
367
304
|
rowGroupControlAfter: bool | None = None,
|
|
368
305
|
columnGroupControlAfter: bool | None = None,
|
|
369
|
-
# Response options
|
|
370
306
|
includeSpreadsheetInResponse: bool = False,
|
|
371
307
|
responseIncludeGridData: bool = False,
|
|
372
308
|
) -> dict[str, Any]:
|
|
@@ -404,80 +340,61 @@ class GoogleSheetApp(APIApplication):
|
|
|
404
340
|
"""
|
|
405
341
|
if not spreadsheetId:
|
|
406
342
|
raise ValueError("spreadsheetId cannot be empty")
|
|
407
|
-
|
|
408
343
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
409
|
-
|
|
410
|
-
# Build the addSheet request with properties
|
|
411
344
|
add_sheet_request = {"properties": {}}
|
|
412
|
-
|
|
413
345
|
if title is not None:
|
|
414
346
|
add_sheet_request["properties"]["title"] = title
|
|
415
|
-
|
|
416
347
|
if sheetId is not None:
|
|
417
348
|
add_sheet_request["properties"]["sheetId"] = sheetId
|
|
418
|
-
|
|
419
349
|
if index is not None:
|
|
420
350
|
add_sheet_request["properties"]["index"] = index
|
|
421
|
-
|
|
422
351
|
if sheetType is not None:
|
|
423
352
|
add_sheet_request["properties"]["sheetType"] = sheetType
|
|
424
|
-
|
|
425
353
|
if hidden is not None:
|
|
426
354
|
add_sheet_request["properties"]["hidden"] = hidden
|
|
427
|
-
|
|
428
355
|
if rightToLeft is not None:
|
|
429
356
|
add_sheet_request["properties"]["rightToLeft"] = rightToLeft
|
|
430
|
-
|
|
431
357
|
if tabColorStyle is not None:
|
|
432
358
|
add_sheet_request["properties"]["tabColorStyle"] = tabColorStyle
|
|
433
|
-
|
|
434
|
-
# Build grid properties if any grid-related parameters are provided
|
|
435
359
|
grid_properties = {}
|
|
436
360
|
if any(
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
361
|
+
(
|
|
362
|
+
param is not None
|
|
363
|
+
for param in [
|
|
364
|
+
rowCount,
|
|
365
|
+
columnCount,
|
|
366
|
+
frozenRowCount,
|
|
367
|
+
frozenColumnCount,
|
|
368
|
+
hideGridlines,
|
|
369
|
+
rowGroupControlAfter,
|
|
370
|
+
columnGroupControlAfter,
|
|
371
|
+
]
|
|
372
|
+
)
|
|
447
373
|
):
|
|
448
374
|
if rowCount is not None:
|
|
449
375
|
grid_properties["rowCount"] = rowCount
|
|
450
|
-
|
|
451
376
|
if columnCount is not None:
|
|
452
377
|
grid_properties["columnCount"] = columnCount
|
|
453
|
-
|
|
454
378
|
if frozenRowCount is not None:
|
|
455
379
|
grid_properties["frozenRowCount"] = frozenRowCount
|
|
456
|
-
|
|
457
380
|
if frozenColumnCount is not None:
|
|
458
381
|
grid_properties["frozenColumnCount"] = frozenColumnCount
|
|
459
|
-
|
|
460
382
|
if hideGridlines is not None:
|
|
461
383
|
grid_properties["hideGridlines"] = hideGridlines
|
|
462
|
-
|
|
463
384
|
if rowGroupControlAfter is not None:
|
|
464
385
|
grid_properties["rowGroupControlAfter"] = rowGroupControlAfter
|
|
465
|
-
|
|
466
386
|
if columnGroupControlAfter is not None:
|
|
467
387
|
grid_properties["columnGroupControlAfter"] = columnGroupControlAfter
|
|
468
|
-
|
|
469
388
|
add_sheet_request["properties"]["gridProperties"] = grid_properties
|
|
470
|
-
|
|
471
389
|
request_body = {
|
|
472
390
|
"requests": [{"addSheet": add_sheet_request}],
|
|
473
391
|
"includeSpreadsheetInResponse": includeSpreadsheetInResponse,
|
|
474
392
|
"responseIncludeGridData": responseIncludeGridData,
|
|
475
393
|
}
|
|
476
|
-
|
|
477
394
|
response = self._post(url, data=request_body)
|
|
478
395
|
return self._handle_response(response)
|
|
479
396
|
|
|
480
|
-
def add_basic_chart(
|
|
397
|
+
async def add_basic_chart(
|
|
481
398
|
self,
|
|
482
399
|
spreadsheetId: str,
|
|
483
400
|
source_sheet_id: int,
|
|
@@ -520,27 +437,17 @@ class GoogleSheetApp(APIApplication):
|
|
|
520
437
|
"""
|
|
521
438
|
if not spreadsheetId:
|
|
522
439
|
raise ValueError("spreadsheetId cannot be empty")
|
|
523
|
-
|
|
524
440
|
if not chart_title:
|
|
525
441
|
raise ValueError("chart_title cannot be empty")
|
|
526
|
-
|
|
527
442
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
528
|
-
|
|
529
|
-
# Build the chart specification
|
|
530
443
|
chart_spec = {
|
|
531
444
|
"title": chart_title,
|
|
532
445
|
"basicChart": {
|
|
533
446
|
"chartType": chart_type,
|
|
534
447
|
"legendPosition": "BOTTOM_LEGEND",
|
|
535
448
|
"axis": [
|
|
536
|
-
{
|
|
537
|
-
|
|
538
|
-
"title": x_axis_title if x_axis_title else "Categories",
|
|
539
|
-
},
|
|
540
|
-
{
|
|
541
|
-
"position": "LEFT_AXIS",
|
|
542
|
-
"title": y_axis_title if y_axis_title else "Values",
|
|
543
|
-
},
|
|
449
|
+
{"position": "BOTTOM_AXIS", "title": x_axis_title if x_axis_title else "Categories"},
|
|
450
|
+
{"position": "LEFT_AXIS", "title": y_axis_title if y_axis_title else "Values"},
|
|
544
451
|
],
|
|
545
452
|
"domains": [
|
|
546
453
|
{
|
|
@@ -549,18 +456,10 @@ class GoogleSheetApp(APIApplication):
|
|
|
549
456
|
"sources": [
|
|
550
457
|
{
|
|
551
458
|
"sheetId": source_sheet_id,
|
|
552
|
-
"startRowIndex": domain_range.get(
|
|
553
|
-
|
|
554
|
-
),
|
|
555
|
-
"
|
|
556
|
-
"endRowIndex", 1
|
|
557
|
-
),
|
|
558
|
-
"startColumnIndex": domain_range.get(
|
|
559
|
-
"startColumnIndex", 0
|
|
560
|
-
),
|
|
561
|
-
"endColumnIndex": domain_range.get(
|
|
562
|
-
"endColumnIndex", 1
|
|
563
|
-
),
|
|
459
|
+
"startRowIndex": domain_range.get("startRowIndex", 0),
|
|
460
|
+
"endRowIndex": domain_range.get("endRowIndex", 1),
|
|
461
|
+
"startColumnIndex": domain_range.get("startColumnIndex", 0),
|
|
462
|
+
"endColumnIndex": domain_range.get("endColumnIndex", 1),
|
|
564
463
|
}
|
|
565
464
|
]
|
|
566
465
|
}
|
|
@@ -571,8 +470,6 @@ class GoogleSheetApp(APIApplication):
|
|
|
571
470
|
"headerCount": 1,
|
|
572
471
|
},
|
|
573
472
|
}
|
|
574
|
-
|
|
575
|
-
# Add series data
|
|
576
473
|
for series_range in series_ranges:
|
|
577
474
|
series = {
|
|
578
475
|
"series": {
|
|
@@ -582,9 +479,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
582
479
|
"sheetId": source_sheet_id,
|
|
583
480
|
"startRowIndex": series_range.get("startRowIndex", 0),
|
|
584
481
|
"endRowIndex": series_range.get("endRowIndex", 1),
|
|
585
|
-
"startColumnIndex": series_range.get(
|
|
586
|
-
"startColumnIndex", 0
|
|
587
|
-
),
|
|
482
|
+
"startColumnIndex": series_range.get("startColumnIndex", 0),
|
|
588
483
|
"endColumnIndex": series_range.get("endColumnIndex", 1),
|
|
589
484
|
}
|
|
590
485
|
]
|
|
@@ -593,40 +488,25 @@ class GoogleSheetApp(APIApplication):
|
|
|
593
488
|
"targetAxis": "LEFT_AXIS",
|
|
594
489
|
}
|
|
595
490
|
chart_spec["basicChart"]["series"].append(series)
|
|
596
|
-
|
|
597
|
-
# Build the position specification
|
|
598
491
|
if new_sheet:
|
|
599
492
|
position_spec = {"newSheet": True}
|
|
600
|
-
# For existing sheet, use overlayPosition structure
|
|
601
493
|
elif chart_position:
|
|
602
494
|
position_spec = chart_position
|
|
603
495
|
else:
|
|
604
|
-
# Default positioning when placing in existing sheet
|
|
605
496
|
position_spec = {
|
|
606
497
|
"overlayPosition": {
|
|
607
|
-
"anchorCell": {
|
|
608
|
-
"sheetId": source_sheet_id,
|
|
609
|
-
"rowIndex": 0,
|
|
610
|
-
"columnIndex": 0,
|
|
611
|
-
},
|
|
498
|
+
"anchorCell": {"sheetId": source_sheet_id, "rowIndex": 0, "columnIndex": 0},
|
|
612
499
|
"offsetXPixels": 0,
|
|
613
500
|
"offsetYPixels": 0,
|
|
614
501
|
"widthPixels": 600,
|
|
615
502
|
"heightPixels": 400,
|
|
616
503
|
}
|
|
617
504
|
}
|
|
618
|
-
|
|
619
|
-
# Build the request body
|
|
620
|
-
request_body = {
|
|
621
|
-
"requests": [
|
|
622
|
-
{"addChart": {"chart": {"spec": chart_spec, "position": position_spec}}}
|
|
623
|
-
]
|
|
624
|
-
}
|
|
625
|
-
|
|
505
|
+
request_body = {"requests": [{"addChart": {"chart": {"spec": chart_spec, "position": position_spec}}}]}
|
|
626
506
|
response = self._post(url, data=request_body)
|
|
627
507
|
return self._handle_response(response)
|
|
628
508
|
|
|
629
|
-
def add_pie_chart(
|
|
509
|
+
async def add_pie_chart(
|
|
630
510
|
self,
|
|
631
511
|
spreadsheetId: str,
|
|
632
512
|
source_sheet_id: int,
|
|
@@ -665,16 +545,11 @@ class GoogleSheetApp(APIApplication):
|
|
|
665
545
|
"""
|
|
666
546
|
if not spreadsheetId:
|
|
667
547
|
raise ValueError("spreadsheetId cannot be empty")
|
|
668
|
-
|
|
669
548
|
if not chart_title:
|
|
670
549
|
raise ValueError("chart_title cannot be empty")
|
|
671
|
-
|
|
672
|
-
if pie_hole is not None and not 0 <= pie_hole <= 1:
|
|
550
|
+
if pie_hole is not None and (not 0 <= pie_hole <= 1):
|
|
673
551
|
raise ValueError("pie_hole must be between 0.0 and 1.0")
|
|
674
|
-
|
|
675
552
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
676
|
-
|
|
677
|
-
# Build the pie chart specification
|
|
678
553
|
pie_chart_spec = {
|
|
679
554
|
"legendPosition": legend_position,
|
|
680
555
|
"domain": {
|
|
@@ -697,55 +572,35 @@ class GoogleSheetApp(APIApplication):
|
|
|
697
572
|
"sheetId": source_sheet_id,
|
|
698
573
|
"startRowIndex": data_range.get("startRowIndex", 0),
|
|
699
574
|
"endRowIndex": data_range.get("endRowIndex", 1),
|
|
700
|
-
"startColumnIndex": data_range.get("startColumnIndex", 0)
|
|
701
|
-
+ 1,
|
|
575
|
+
"startColumnIndex": data_range.get("startColumnIndex", 0) + 1,
|
|
702
576
|
"endColumnIndex": data_range.get("endColumnIndex", 2),
|
|
703
577
|
}
|
|
704
578
|
]
|
|
705
579
|
}
|
|
706
580
|
},
|
|
707
581
|
}
|
|
708
|
-
|
|
709
|
-
# Add pie hole for donut chart if specified
|
|
710
582
|
if pie_hole is not None:
|
|
711
583
|
pie_chart_spec["pieHole"] = pie_hole
|
|
712
|
-
|
|
713
|
-
# Build the chart specification
|
|
714
584
|
chart_spec = {"title": chart_title, "pieChart": pie_chart_spec}
|
|
715
|
-
|
|
716
|
-
# Build the position specification
|
|
717
585
|
if new_sheet:
|
|
718
586
|
position_spec = {"newSheet": True}
|
|
719
|
-
# For existing sheet, use overlayPosition structure
|
|
720
587
|
elif chart_position:
|
|
721
588
|
position_spec = chart_position
|
|
722
589
|
else:
|
|
723
|
-
# Default positioning when placing in existing sheet
|
|
724
590
|
position_spec = {
|
|
725
591
|
"overlayPosition": {
|
|
726
|
-
"anchorCell": {
|
|
727
|
-
"sheetId": source_sheet_id,
|
|
728
|
-
"rowIndex": 0,
|
|
729
|
-
"columnIndex": 0,
|
|
730
|
-
},
|
|
592
|
+
"anchorCell": {"sheetId": source_sheet_id, "rowIndex": 0, "columnIndex": 0},
|
|
731
593
|
"offsetXPixels": 0,
|
|
732
594
|
"offsetYPixels": 0,
|
|
733
595
|
"widthPixels": 600,
|
|
734
596
|
"heightPixels": 400,
|
|
735
597
|
}
|
|
736
598
|
}
|
|
737
|
-
|
|
738
|
-
# Build the request body
|
|
739
|
-
request_body = {
|
|
740
|
-
"requests": [
|
|
741
|
-
{"addChart": {"chart": {"spec": chart_spec, "position": position_spec}}}
|
|
742
|
-
]
|
|
743
|
-
}
|
|
744
|
-
|
|
599
|
+
request_body = {"requests": [{"addChart": {"chart": {"spec": chart_spec, "position": position_spec}}}]}
|
|
745
600
|
response = self._post(url, data=request_body)
|
|
746
601
|
return self._handle_response(response)
|
|
747
602
|
|
|
748
|
-
def add_table(
|
|
603
|
+
async def add_table(
|
|
749
604
|
self,
|
|
750
605
|
spreadsheetId: str,
|
|
751
606
|
sheet_id: int,
|
|
@@ -785,51 +640,24 @@ class GoogleSheetApp(APIApplication):
|
|
|
785
640
|
"""
|
|
786
641
|
if not spreadsheetId:
|
|
787
642
|
raise ValueError("spreadsheetId cannot be empty")
|
|
788
|
-
|
|
789
643
|
if not table_name:
|
|
790
644
|
raise ValueError("table_name cannot be empty")
|
|
791
|
-
|
|
792
645
|
if not table_id:
|
|
793
646
|
raise ValueError("table_id cannot be empty")
|
|
794
|
-
|
|
795
|
-
if (
|
|
796
|
-
start_row_index < 0
|
|
797
|
-
or end_row_index < 0
|
|
798
|
-
or start_column_index < 0
|
|
799
|
-
or end_column_index < 0
|
|
800
|
-
):
|
|
647
|
+
if start_row_index < 0 or end_row_index < 0 or start_column_index < 0 or (end_column_index < 0):
|
|
801
648
|
raise ValueError("All indices must be non-negative")
|
|
802
|
-
|
|
803
649
|
if start_row_index >= end_row_index:
|
|
804
650
|
raise ValueError("end_row_index must be greater than start_row_index")
|
|
805
|
-
|
|
806
651
|
if start_column_index >= end_column_index:
|
|
807
652
|
raise ValueError("end_column_index must be greater than start_column_index")
|
|
808
|
-
|
|
809
|
-
# Validate column properties if provided
|
|
810
653
|
if column_properties:
|
|
811
|
-
valid_column_types = [
|
|
812
|
-
"TEXT",
|
|
813
|
-
"PERCENT",
|
|
814
|
-
"DROPDOWN",
|
|
815
|
-
"DOUBLE",
|
|
816
|
-
"CURRENCY",
|
|
817
|
-
"DATE",
|
|
818
|
-
"TIME",
|
|
819
|
-
"DATE_TIME",
|
|
820
|
-
]
|
|
654
|
+
valid_column_types = ["TEXT", "PERCENT", "DROPDOWN", "DOUBLE", "CURRENCY", "DATE", "TIME", "DATE_TIME"]
|
|
821
655
|
for i, prop in enumerate(column_properties):
|
|
822
|
-
if
|
|
823
|
-
"columnType" in prop
|
|
824
|
-
and prop["columnType"] not in valid_column_types
|
|
825
|
-
):
|
|
656
|
+
if "columnType" in prop and prop["columnType"] not in valid_column_types:
|
|
826
657
|
raise ValueError(
|
|
827
658
|
f"Invalid column type '{prop['columnType']}' at index {i}. Valid types are: {', '.join(valid_column_types)}"
|
|
828
659
|
)
|
|
829
|
-
|
|
830
660
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
831
|
-
|
|
832
|
-
# Build the table specification
|
|
833
661
|
table_spec = {
|
|
834
662
|
"name": table_name,
|
|
835
663
|
"tableId": table_id,
|
|
@@ -841,18 +669,13 @@ class GoogleSheetApp(APIApplication):
|
|
|
841
669
|
"endRowIndex": end_row_index,
|
|
842
670
|
},
|
|
843
671
|
}
|
|
844
|
-
|
|
845
|
-
# Add column properties if provided
|
|
846
672
|
if column_properties:
|
|
847
673
|
table_spec["columnProperties"] = column_properties
|
|
848
|
-
|
|
849
|
-
# Build the request body
|
|
850
674
|
request_body = {"requests": [{"addTable": {"table": table_spec}}]}
|
|
851
|
-
|
|
852
675
|
response = self._post(url, data=request_body)
|
|
853
676
|
return self._handle_response(response)
|
|
854
677
|
|
|
855
|
-
def update_table(
|
|
678
|
+
async def update_table(
|
|
856
679
|
self,
|
|
857
680
|
spreadsheetId: str,
|
|
858
681
|
table_id: str,
|
|
@@ -891,70 +714,33 @@ class GoogleSheetApp(APIApplication):
|
|
|
891
714
|
"""
|
|
892
715
|
if not spreadsheetId:
|
|
893
716
|
raise ValueError("spreadsheetId cannot be empty")
|
|
894
|
-
|
|
895
717
|
if not table_id:
|
|
896
718
|
raise ValueError("table_id cannot be empty")
|
|
897
|
-
|
|
898
|
-
# Validate indices if provided
|
|
899
719
|
if start_row_index is not None and start_row_index < 0:
|
|
900
720
|
raise ValueError("start_row_index must be non-negative")
|
|
901
|
-
|
|
902
721
|
if end_row_index is not None and end_row_index < 0:
|
|
903
722
|
raise ValueError("end_row_index must be non-negative")
|
|
904
|
-
|
|
905
723
|
if start_column_index is not None and start_column_index < 0:
|
|
906
724
|
raise ValueError("start_column_index must be non-negative")
|
|
907
|
-
|
|
908
725
|
if end_column_index is not None and end_column_index < 0:
|
|
909
726
|
raise ValueError("end_column_index must be non-negative")
|
|
910
|
-
|
|
911
|
-
if (
|
|
912
|
-
start_row_index is not None
|
|
913
|
-
and end_row_index is not None
|
|
914
|
-
and start_row_index >= end_row_index
|
|
915
|
-
):
|
|
727
|
+
if start_row_index is not None and end_row_index is not None and (start_row_index >= end_row_index):
|
|
916
728
|
raise ValueError("end_row_index must be greater than start_row_index")
|
|
917
|
-
|
|
918
|
-
if (
|
|
919
|
-
start_column_index is not None
|
|
920
|
-
and end_column_index is not None
|
|
921
|
-
and start_column_index >= end_column_index
|
|
922
|
-
):
|
|
729
|
+
if start_column_index is not None and end_column_index is not None and (start_column_index >= end_column_index):
|
|
923
730
|
raise ValueError("end_column_index must be greater than start_column_index")
|
|
924
|
-
|
|
925
|
-
# Validate column properties if provided
|
|
926
731
|
if column_properties:
|
|
927
|
-
valid_column_types = [
|
|
928
|
-
"TEXT",
|
|
929
|
-
"PERCENT",
|
|
930
|
-
"DROPDOWN",
|
|
931
|
-
"DOUBLE",
|
|
932
|
-
"CURRENCY",
|
|
933
|
-
"DATE",
|
|
934
|
-
"TIME",
|
|
935
|
-
"DATE_TIME",
|
|
936
|
-
]
|
|
732
|
+
valid_column_types = ["TEXT", "PERCENT", "DROPDOWN", "DOUBLE", "CURRENCY", "DATE", "TIME", "DATE_TIME"]
|
|
937
733
|
for i, prop in enumerate(column_properties):
|
|
938
|
-
if
|
|
939
|
-
"columnType" in prop
|
|
940
|
-
and prop["columnType"] not in valid_column_types
|
|
941
|
-
):
|
|
734
|
+
if "columnType" in prop and prop["columnType"] not in valid_column_types:
|
|
942
735
|
raise ValueError(
|
|
943
736
|
f"Invalid column type '{prop['columnType']}' at index {i}. Valid types are: {', '.join(valid_column_types)}"
|
|
944
737
|
)
|
|
945
|
-
|
|
946
738
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
947
|
-
|
|
948
|
-
# Build the table specification and track fields to update
|
|
949
739
|
table_spec: dict[str, Any] = {"tableId": table_id}
|
|
950
740
|
fields_to_update = []
|
|
951
|
-
|
|
952
|
-
# Add optional properties if provided
|
|
953
741
|
if table_name is not None:
|
|
954
742
|
table_spec["name"] = table_name
|
|
955
743
|
fields_to_update.append("name")
|
|
956
|
-
|
|
957
|
-
# Build range if any range parameters are provided
|
|
958
744
|
range_params: dict[str, Any] = {}
|
|
959
745
|
if start_row_index is not None:
|
|
960
746
|
range_params["startRowIndex"] = start_row_index
|
|
@@ -964,38 +750,19 @@ class GoogleSheetApp(APIApplication):
|
|
|
964
750
|
range_params["startColumnIndex"] = start_column_index
|
|
965
751
|
if end_column_index is not None:
|
|
966
752
|
range_params["endColumnIndex"] = end_column_index
|
|
967
|
-
|
|
968
753
|
if range_params:
|
|
969
754
|
table_spec["range"] = range_params
|
|
970
755
|
fields_to_update.append("range")
|
|
971
|
-
|
|
972
|
-
# Add column properties if provided
|
|
973
756
|
if column_properties:
|
|
974
757
|
table_spec["columnProperties"] = column_properties
|
|
975
758
|
fields_to_update.append("columnProperties")
|
|
976
|
-
|
|
977
|
-
# Validate that at least one field is being updated
|
|
978
759
|
if not fields_to_update:
|
|
979
|
-
raise ValueError(
|
|
980
|
-
|
|
981
|
-
)
|
|
982
|
-
|
|
983
|
-
# Build the request body
|
|
984
|
-
request_body = {
|
|
985
|
-
"requests": [
|
|
986
|
-
{
|
|
987
|
-
"updateTable": {
|
|
988
|
-
"table": table_spec,
|
|
989
|
-
"fields": ",".join(fields_to_update),
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
]
|
|
993
|
-
}
|
|
994
|
-
|
|
760
|
+
raise ValueError("At least one field must be provided for update (table_name, range indices, or column_properties)")
|
|
761
|
+
request_body = {"requests": [{"updateTable": {"table": table_spec, "fields": ",".join(fields_to_update)}}]}
|
|
995
762
|
response = self._post(url, data=request_body)
|
|
996
763
|
return self._handle_response(response)
|
|
997
764
|
|
|
998
|
-
def clear_values(self, spreadsheetId: str, range: str) -> dict[str, Any]:
|
|
765
|
+
async def clear_values(self, spreadsheetId: str, range: str) -> dict[str, Any]:
|
|
999
766
|
"""
|
|
1000
767
|
Clears data from a single, specified cell range while preserving all formatting. Unlike `delete_dimensions`, it only removes content, not the cells themselves. For clearing multiple ranges simultaneously, use the `batch_clear_values` function.
|
|
1001
768
|
|
|
@@ -1017,12 +784,8 @@ class GoogleSheetApp(APIApplication):
|
|
|
1017
784
|
response = self._post(url, data={})
|
|
1018
785
|
return self._handle_response(response)
|
|
1019
786
|
|
|
1020
|
-
def update_values(
|
|
1021
|
-
self,
|
|
1022
|
-
spreadsheetId: str,
|
|
1023
|
-
range: str,
|
|
1024
|
-
values: list[list[Any]],
|
|
1025
|
-
value_input_option: str = "RAW",
|
|
787
|
+
async def update_values(
|
|
788
|
+
self, spreadsheetId: str, range: str, values: list[list[Any]], value_input_option: str = "RAW"
|
|
1026
789
|
) -> dict[str, Any]:
|
|
1027
790
|
"""
|
|
1028
791
|
Overwrites cell values within a specific A1 notation range using a provided 2D list. This function replaces existing data in a predefined area, distinguishing it from `append_values`, which adds new rows after a table instead of overwriting a specific block of cells.
|
|
@@ -1049,11 +812,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
1049
812
|
response = self._put(url, data=data, params=params)
|
|
1050
813
|
return self._handle_response(response)
|
|
1051
814
|
|
|
1052
|
-
def batch_clear_values(
|
|
1053
|
-
self,
|
|
1054
|
-
spreadsheetId: str,
|
|
1055
|
-
ranges: list[str],
|
|
1056
|
-
) -> dict[str, Any]:
|
|
815
|
+
async def batch_clear_values(self, spreadsheetId: str, ranges: list[str]) -> dict[str, Any]:
|
|
1057
816
|
"""
|
|
1058
817
|
Clears cell values from multiple specified ranges in a single batch operation, preserving existing formatting. Unlike `clear_values`, which handles a single range, this method efficiently processes a list of ranges at once, removing only the content and not the cells themselves.
|
|
1059
818
|
|
|
@@ -1073,18 +832,14 @@ class GoogleSheetApp(APIApplication):
|
|
|
1073
832
|
"""
|
|
1074
833
|
if not spreadsheetId:
|
|
1075
834
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1076
|
-
|
|
1077
835
|
if not ranges or not isinstance(ranges, list) or len(ranges) == 0:
|
|
1078
836
|
raise ValueError("ranges must be a non-empty list")
|
|
1079
|
-
|
|
1080
837
|
url = f"{self.base_url}/{spreadsheetId}/values:batchClear"
|
|
1081
|
-
|
|
1082
838
|
request_body = {"ranges": ranges}
|
|
1083
|
-
|
|
1084
839
|
response = self._post(url, data=request_body)
|
|
1085
840
|
return self._handle_response(response)
|
|
1086
841
|
|
|
1087
|
-
def batch_get_values_by_data_filter(
|
|
842
|
+
async def batch_get_values_by_data_filter(
|
|
1088
843
|
self,
|
|
1089
844
|
spreadsheetId: str,
|
|
1090
845
|
data_filters: list[dict],
|
|
@@ -1116,57 +871,26 @@ class GoogleSheetApp(APIApplication):
|
|
|
1116
871
|
"""
|
|
1117
872
|
if not spreadsheetId:
|
|
1118
873
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1119
|
-
|
|
1120
|
-
if (
|
|
1121
|
-
not data_filters
|
|
1122
|
-
or not isinstance(data_filters, list)
|
|
1123
|
-
or len(data_filters) == 0
|
|
1124
|
-
):
|
|
874
|
+
if not data_filters or not isinstance(data_filters, list) or len(data_filters) == 0:
|
|
1125
875
|
raise ValueError("data_filters must be a non-empty list")
|
|
1126
|
-
|
|
1127
876
|
if major_dimension and major_dimension not in ["ROWS", "COLUMNS"]:
|
|
1128
877
|
raise ValueError('major_dimension must be either "ROWS" or "COLUMNS"')
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
"
|
|
1133
|
-
"FORMULA",
|
|
1134
|
-
]:
|
|
1135
|
-
raise ValueError(
|
|
1136
|
-
'value_render_option must be either "FORMATTED_VALUE", "UNFORMATTED_VALUE", or "FORMULA"'
|
|
1137
|
-
)
|
|
1138
|
-
|
|
1139
|
-
if date_time_render_option and date_time_render_option not in [
|
|
1140
|
-
"SERIAL_NUMBER",
|
|
1141
|
-
"FORMATTED_STRING",
|
|
1142
|
-
]:
|
|
1143
|
-
raise ValueError(
|
|
1144
|
-
'date_time_render_option must be either "SERIAL_NUMBER" or "FORMATTED_STRING"'
|
|
1145
|
-
)
|
|
1146
|
-
|
|
878
|
+
if value_render_option and value_render_option not in ["FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA"]:
|
|
879
|
+
raise ValueError('value_render_option must be either "FORMATTED_VALUE", "UNFORMATTED_VALUE", or "FORMULA"')
|
|
880
|
+
if date_time_render_option and date_time_render_option not in ["SERIAL_NUMBER", "FORMATTED_STRING"]:
|
|
881
|
+
raise ValueError('date_time_render_option must be either "SERIAL_NUMBER" or "FORMATTED_STRING"')
|
|
1147
882
|
url = f"{self.base_url}/{spreadsheetId}/values:batchGetByDataFilter"
|
|
1148
|
-
|
|
1149
883
|
request_body: dict[str, Any] = {"dataFilters": data_filters}
|
|
1150
|
-
|
|
1151
|
-
# Add optional parameters if provided
|
|
1152
884
|
if major_dimension:
|
|
1153
885
|
request_body["majorDimension"] = major_dimension
|
|
1154
|
-
|
|
1155
886
|
if value_render_option:
|
|
1156
887
|
request_body["valueRenderOption"] = value_render_option
|
|
1157
|
-
|
|
1158
888
|
if date_time_render_option:
|
|
1159
889
|
request_body["dateTimeRenderOption"] = date_time_render_option
|
|
1160
|
-
|
|
1161
890
|
response = self._post(url, data=request_body)
|
|
1162
891
|
return self._handle_response(response)
|
|
1163
892
|
|
|
1164
|
-
def copy_sheet_to_spreadsheet(
|
|
1165
|
-
self,
|
|
1166
|
-
spreadsheetId: str,
|
|
1167
|
-
sheet_id: int,
|
|
1168
|
-
destination_spreadsheetId: str,
|
|
1169
|
-
) -> dict[str, Any]:
|
|
893
|
+
async def copy_sheet_to_spreadsheet(self, spreadsheetId: str, sheet_id: int, destination_spreadsheetId: str) -> dict[str, Any]:
|
|
1170
894
|
"""
|
|
1171
895
|
Copies a specific sheet, including all its data and formatting, from a source spreadsheet to a different destination spreadsheet. This action duplicates an entire worksheet into another workbook, returning properties of the newly created sheet.
|
|
1172
896
|
|
|
@@ -1188,21 +912,16 @@ class GoogleSheetApp(APIApplication):
|
|
|
1188
912
|
"""
|
|
1189
913
|
if not spreadsheetId:
|
|
1190
914
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1191
|
-
|
|
1192
915
|
if sheet_id is None:
|
|
1193
916
|
raise ValueError("sheet_id cannot be empty")
|
|
1194
|
-
|
|
1195
917
|
if not destination_spreadsheetId:
|
|
1196
918
|
raise ValueError("destination_spreadsheetId cannot be empty")
|
|
1197
|
-
|
|
1198
919
|
url = f"{self.base_url}/{spreadsheetId}/sheets/{sheet_id}:copyTo"
|
|
1199
|
-
|
|
1200
920
|
request_body = {"destinationSpreadsheetId": destination_spreadsheetId}
|
|
1201
|
-
|
|
1202
921
|
response = self._post(url, data=request_body)
|
|
1203
922
|
return self._handle_response(response)
|
|
1204
923
|
|
|
1205
|
-
def write_values_to_sheet(
|
|
924
|
+
async def write_values_to_sheet(
|
|
1206
925
|
self,
|
|
1207
926
|
spreadsheetId: str,
|
|
1208
927
|
sheet_name: str,
|
|
@@ -1234,39 +953,23 @@ class GoogleSheetApp(APIApplication):
|
|
|
1234
953
|
"""
|
|
1235
954
|
if not spreadsheetId:
|
|
1236
955
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1237
|
-
|
|
1238
956
|
if not sheet_name:
|
|
1239
957
|
raise ValueError("sheet_name cannot be empty")
|
|
1240
|
-
|
|
1241
958
|
if not values or not isinstance(values, list) or len(values) == 0:
|
|
1242
959
|
raise ValueError("values must be a non-empty 2D list")
|
|
1243
|
-
|
|
1244
960
|
if value_input_option not in ["RAW", "USER_ENTERED"]:
|
|
1245
|
-
raise ValueError(
|
|
1246
|
-
'value_input_option must be either "RAW" or "USER_ENTERED"'
|
|
1247
|
-
)
|
|
1248
|
-
|
|
1249
|
-
# Determine the range based on first_cell_location
|
|
961
|
+
raise ValueError('value_input_option must be either "RAW" or "USER_ENTERED"')
|
|
1250
962
|
if first_cell_location:
|
|
1251
|
-
# Update specific range starting from first_cell_location
|
|
1252
963
|
range_str = f"{sheet_name}!{first_cell_location}"
|
|
1253
964
|
else:
|
|
1254
|
-
# Append to the sheet (no specific range)
|
|
1255
965
|
range_str = f"{sheet_name}"
|
|
1256
|
-
|
|
1257
966
|
url = f"{self.base_url}/{spreadsheetId}/values/{range_str}"
|
|
1258
|
-
|
|
1259
|
-
params = {
|
|
1260
|
-
"valueInputOption": value_input_option,
|
|
1261
|
-
"includeValuesInResponse": include_values_in_response,
|
|
1262
|
-
}
|
|
1263
|
-
|
|
967
|
+
params = {"valueInputOption": value_input_option, "includeValuesInResponse": include_values_in_response}
|
|
1264
968
|
data = {"values": values}
|
|
1265
|
-
|
|
1266
969
|
response = self._put(url, data=data, params=params)
|
|
1267
970
|
return self._handle_response(response)
|
|
1268
971
|
|
|
1269
|
-
def append_values(
|
|
972
|
+
async def append_values(
|
|
1270
973
|
self,
|
|
1271
974
|
spreadsheetId: str,
|
|
1272
975
|
range: str,
|
|
@@ -1302,74 +1005,35 @@ class GoogleSheetApp(APIApplication):
|
|
|
1302
1005
|
"""
|
|
1303
1006
|
if not spreadsheetId:
|
|
1304
1007
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1305
|
-
|
|
1306
1008
|
if not range:
|
|
1307
1009
|
raise ValueError("range cannot be empty")
|
|
1308
|
-
|
|
1309
1010
|
if not value_input_option:
|
|
1310
1011
|
raise ValueError("value_input_option cannot be empty")
|
|
1311
|
-
|
|
1312
1012
|
if value_input_option not in ["RAW", "USER_ENTERED"]:
|
|
1313
|
-
raise ValueError(
|
|
1314
|
-
'value_input_option must be either "RAW" or "USER_ENTERED"'
|
|
1315
|
-
)
|
|
1316
|
-
|
|
1013
|
+
raise ValueError('value_input_option must be either "RAW" or "USER_ENTERED"')
|
|
1317
1014
|
if not values or not isinstance(values, list) or len(values) == 0:
|
|
1318
1015
|
raise ValueError("values must be a non-empty 2D list")
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
"
|
|
1323
|
-
]:
|
|
1324
|
-
raise ValueError(
|
|
1325
|
-
'insert_data_option must be either "OVERWRITE" or "INSERT_ROWS"'
|
|
1326
|
-
)
|
|
1327
|
-
|
|
1328
|
-
if response_value_render_option and response_value_render_option not in [
|
|
1329
|
-
"FORMATTED_VALUE",
|
|
1330
|
-
"UNFORMATTED_VALUE",
|
|
1331
|
-
"FORMULA",
|
|
1332
|
-
]:
|
|
1333
|
-
raise ValueError(
|
|
1334
|
-
'response_value_render_option must be either "FORMATTED_VALUE", "UNFORMATTED_VALUE", or "FORMULA"'
|
|
1335
|
-
)
|
|
1336
|
-
|
|
1337
|
-
if (
|
|
1338
|
-
response_date_time_render_option
|
|
1339
|
-
and response_date_time_render_option
|
|
1340
|
-
not in ["SERIAL_NUMBER", "FORMATTED_STRING"]
|
|
1341
|
-
):
|
|
1342
|
-
raise ValueError(
|
|
1343
|
-
'response_date_time_render_option must be either "SERIAL_NUMBER" or "FORMATTED_STRING"'
|
|
1344
|
-
)
|
|
1345
|
-
|
|
1016
|
+
if insert_data_option and insert_data_option not in ["OVERWRITE", "INSERT_ROWS"]:
|
|
1017
|
+
raise ValueError('insert_data_option must be either "OVERWRITE" or "INSERT_ROWS"')
|
|
1018
|
+
if response_value_render_option and response_value_render_option not in ["FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA"]:
|
|
1019
|
+
raise ValueError('response_value_render_option must be either "FORMATTED_VALUE", "UNFORMATTED_VALUE", or "FORMULA"')
|
|
1020
|
+
if response_date_time_render_option and response_date_time_render_option not in ["SERIAL_NUMBER", "FORMATTED_STRING"]:
|
|
1021
|
+
raise ValueError('response_date_time_render_option must be either "SERIAL_NUMBER" or "FORMATTED_STRING"')
|
|
1346
1022
|
url = f"{self.base_url}/{spreadsheetId}/values/{range}:append"
|
|
1347
|
-
|
|
1348
1023
|
params: dict[str, Any] = {"valueInputOption": value_input_option}
|
|
1349
|
-
|
|
1350
|
-
# Add optional parameters if provided
|
|
1351
1024
|
if insert_data_option:
|
|
1352
1025
|
params["insertDataOption"] = insert_data_option
|
|
1353
|
-
|
|
1354
1026
|
if include_values_in_response is not None:
|
|
1355
1027
|
params["includeValuesInResponse"] = include_values_in_response
|
|
1356
|
-
|
|
1357
1028
|
if response_value_render_option:
|
|
1358
1029
|
params["responseValueRenderOption"] = response_value_render_option
|
|
1359
|
-
|
|
1360
1030
|
if response_date_time_render_option:
|
|
1361
1031
|
params["responseDateTimeRenderOption"] = response_date_time_render_option
|
|
1362
|
-
|
|
1363
1032
|
data = {"values": values}
|
|
1364
|
-
|
|
1365
1033
|
response = self._post(url, data=data, params=params)
|
|
1366
1034
|
return self._handle_response(response)
|
|
1367
1035
|
|
|
1368
|
-
def clear_basic_filter(
|
|
1369
|
-
self,
|
|
1370
|
-
spreadsheetId: str,
|
|
1371
|
-
sheet_id: int,
|
|
1372
|
-
) -> dict[str, Any]:
|
|
1036
|
+
async def clear_basic_filter(self, spreadsheetId: str, sheet_id: int) -> dict[str, Any]:
|
|
1373
1037
|
"""
|
|
1374
1038
|
Removes the basic filter from a specified sheet, clearing active sorting and filtering criteria to restore the default data view. As the direct counterpart to `set_basic_filter`, this function removes the entire filter object, not just the cell content.
|
|
1375
1039
|
|
|
@@ -1389,22 +1053,14 @@ class GoogleSheetApp(APIApplication):
|
|
|
1389
1053
|
"""
|
|
1390
1054
|
if not spreadsheetId:
|
|
1391
1055
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1392
|
-
|
|
1393
1056
|
if sheet_id < 0:
|
|
1394
1057
|
raise ValueError("sheet_id must be non-negative")
|
|
1395
|
-
|
|
1396
1058
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
1397
|
-
|
|
1398
1059
|
request_body = {"requests": [{"clearBasicFilter": {"sheetId": sheet_id}}]}
|
|
1399
|
-
|
|
1400
1060
|
response = self._post(url, data=request_body)
|
|
1401
1061
|
return self._handle_response(response)
|
|
1402
1062
|
|
|
1403
|
-
def delete_sheet(
|
|
1404
|
-
self,
|
|
1405
|
-
spreadsheetId: str,
|
|
1406
|
-
sheet_id: int,
|
|
1407
|
-
) -> dict[str, Any]:
|
|
1063
|
+
async def delete_sheet(self, spreadsheetId: str, sheet_id: int) -> dict[str, Any]:
|
|
1408
1064
|
"""
|
|
1409
1065
|
Permanently deletes a specific sheet (worksheet) from a Google Spreadsheet using its sheet ID. This operation removes the target sheet and all its contents, acting as the direct counterpart to the `add_sheet` function which creates new sheets within a spreadsheet.
|
|
1410
1066
|
|
|
@@ -1424,23 +1080,15 @@ class GoogleSheetApp(APIApplication):
|
|
|
1424
1080
|
"""
|
|
1425
1081
|
if not spreadsheetId:
|
|
1426
1082
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1427
|
-
|
|
1428
1083
|
if sheet_id < 0:
|
|
1429
1084
|
raise ValueError("sheet_id must be non-negative")
|
|
1430
|
-
|
|
1431
1085
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
1432
|
-
|
|
1433
1086
|
request_body = {"requests": [{"deleteSheet": {"sheetId": sheet_id}}]}
|
|
1434
|
-
|
|
1435
1087
|
response = self._post(url, data=request_body)
|
|
1436
1088
|
return self._handle_response(response)
|
|
1437
1089
|
|
|
1438
|
-
def discover_tables(
|
|
1439
|
-
self,
|
|
1440
|
-
spreadsheetId: str,
|
|
1441
|
-
min_rows: int = 2,
|
|
1442
|
-
min_columns: int = 1,
|
|
1443
|
-
min_confidence: float = 0.5,
|
|
1090
|
+
async def discover_tables(
|
|
1091
|
+
self, spreadsheetId: str, min_rows: int = 2, min_columns: int = 1, min_confidence: float = 0.5
|
|
1444
1092
|
) -> dict[str, Any]:
|
|
1445
1093
|
"""
|
|
1446
1094
|
Heuristically analyzes a spreadsheet to discover and list all table-like data structures, identifying headers and data boundaries. It returns informal data blocks meeting specified size criteria, distinguishing it from functions like `add_table` that manage formally defined tables.
|
|
@@ -1463,55 +1111,31 @@ class GoogleSheetApp(APIApplication):
|
|
|
1463
1111
|
"""
|
|
1464
1112
|
if not spreadsheetId:
|
|
1465
1113
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1466
|
-
|
|
1467
1114
|
if min_rows < 1:
|
|
1468
1115
|
raise ValueError("min_rows must be at least 1")
|
|
1469
|
-
|
|
1470
1116
|
if min_columns < 1:
|
|
1471
1117
|
raise ValueError("min_columns must be at least 1")
|
|
1472
|
-
|
|
1473
1118
|
if not 0 <= min_confidence <= 1:
|
|
1474
1119
|
raise ValueError("min_confidence must be between 0.0 and 1.0")
|
|
1475
|
-
|
|
1476
|
-
# Get spreadsheet structure
|
|
1477
1120
|
spreadsheet = self.get_spreadsheet_metadata(spreadsheetId)
|
|
1478
|
-
|
|
1479
1121
|
tables = []
|
|
1480
|
-
|
|
1481
1122
|
for sheet in spreadsheet.get("sheets", []):
|
|
1482
1123
|
sheet_properties = sheet.get("properties", {})
|
|
1483
1124
|
sheet_id = sheet_properties.get("sheetId")
|
|
1484
1125
|
sheet_title = sheet_properties.get("title", "Sheet1")
|
|
1485
|
-
|
|
1486
|
-
# Analyze sheet for tables using helper function
|
|
1487
1126
|
sheet_tables = analyze_sheet_for_tables(
|
|
1488
|
-
self.get_values,
|
|
1489
|
-
spreadsheetId,
|
|
1490
|
-
sheet_id,
|
|
1491
|
-
sheet_title,
|
|
1492
|
-
min_rows,
|
|
1493
|
-
min_columns,
|
|
1494
|
-
min_confidence,
|
|
1127
|
+
self.get_values, spreadsheetId, sheet_id, sheet_title, min_rows, min_columns, min_confidence
|
|
1495
1128
|
)
|
|
1496
|
-
|
|
1497
1129
|
tables.extend(sheet_tables)
|
|
1498
|
-
|
|
1499
1130
|
return {
|
|
1500
1131
|
"spreadsheetId": spreadsheetId,
|
|
1501
1132
|
"total_tables": len(tables),
|
|
1502
1133
|
"tables": tables,
|
|
1503
|
-
"analysis_parameters": {
|
|
1504
|
-
"min_rows": min_rows,
|
|
1505
|
-
"min_columns": min_columns,
|
|
1506
|
-
},
|
|
1134
|
+
"analysis_parameters": {"min_rows": min_rows, "min_columns": min_columns},
|
|
1507
1135
|
}
|
|
1508
1136
|
|
|
1509
|
-
def analyze_table_schema(
|
|
1510
|
-
self,
|
|
1511
|
-
spreadsheetId: str,
|
|
1512
|
-
table_name: str,
|
|
1513
|
-
sheet_name: str | None = None,
|
|
1514
|
-
sample_size: int = 50,
|
|
1137
|
+
async def analyze_table_schema(
|
|
1138
|
+
self, spreadsheetId: str, table_name: str, sheet_name: str | None = None, sample_size: int = 50
|
|
1515
1139
|
) -> dict[str, Any]:
|
|
1516
1140
|
"""
|
|
1517
1141
|
Infers a specified table's schema by analyzing a data sample. After locating the table by name (a value discovered via `discover_tables`), this function determines the most likely data type and properties for each column, providing a detailed structural breakdown of its content.
|
|
@@ -1534,28 +1158,17 @@ class GoogleSheetApp(APIApplication):
|
|
|
1534
1158
|
"""
|
|
1535
1159
|
if not spreadsheetId:
|
|
1536
1160
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1537
|
-
|
|
1538
1161
|
if not table_name:
|
|
1539
1162
|
raise ValueError("table_name cannot be empty")
|
|
1540
|
-
|
|
1541
1163
|
if not 1 <= sample_size <= 1000:
|
|
1542
1164
|
raise ValueError("sample_size must be between 1 and 1000")
|
|
1543
|
-
|
|
1544
|
-
# Get spreadsheet structure
|
|
1545
1165
|
spreadsheet = self.get_spreadsheet_metadata(spreadsheetId)
|
|
1546
|
-
|
|
1547
|
-
# Find the target table
|
|
1548
1166
|
target_table = None
|
|
1549
|
-
|
|
1550
1167
|
for sheet in spreadsheet.get("sheets", []):
|
|
1551
1168
|
sheet_properties = sheet.get("properties", {})
|
|
1552
1169
|
sheet_title = sheet_properties.get("title", "Sheet1")
|
|
1553
|
-
|
|
1554
|
-
# If sheet_name is specified, only look in that sheet
|
|
1555
1170
|
if sheet_name and sheet_title != sheet_name:
|
|
1556
1171
|
continue
|
|
1557
|
-
|
|
1558
|
-
# Get tables in this sheet
|
|
1559
1172
|
sheet_tables = analyze_sheet_for_tables(
|
|
1560
1173
|
self.get_values,
|
|
1561
1174
|
spreadsheetId,
|
|
@@ -1565,35 +1178,20 @@ class GoogleSheetApp(APIApplication):
|
|
|
1565
1178
|
min_columns=1,
|
|
1566
1179
|
min_confidence=0.3,
|
|
1567
1180
|
)
|
|
1568
|
-
|
|
1569
1181
|
for table in sheet_tables:
|
|
1570
1182
|
if table_name == "auto":
|
|
1571
|
-
|
|
1572
|
-
if target_table is None or (
|
|
1573
|
-
table["rows"] * table["columns"]
|
|
1574
|
-
> target_table["rows"] * target_table["columns"]
|
|
1575
|
-
):
|
|
1183
|
+
if target_table is None or table["rows"] * table["columns"] > target_table["rows"] * target_table["columns"]:
|
|
1576
1184
|
target_table = table
|
|
1577
1185
|
elif table["table_name"] == table_name:
|
|
1578
1186
|
target_table = table
|
|
1579
1187
|
break
|
|
1580
|
-
|
|
1581
1188
|
if target_table and table_name != "auto":
|
|
1582
1189
|
break
|
|
1583
|
-
|
|
1584
1190
|
if not target_table:
|
|
1585
1191
|
raise ValueError(f"Table '{table_name}' not found in spreadsheet")
|
|
1192
|
+
return analyze_table_schema(self.get_values, spreadsheetId, target_table, sample_size)
|
|
1586
1193
|
|
|
1587
|
-
|
|
1588
|
-
return analyze_table_schema(
|
|
1589
|
-
self.get_values, spreadsheetId, target_table, sample_size
|
|
1590
|
-
)
|
|
1591
|
-
|
|
1592
|
-
def set_basic_filter(
|
|
1593
|
-
self,
|
|
1594
|
-
spreadsheetId: str,
|
|
1595
|
-
filter: dict,
|
|
1596
|
-
) -> dict[str, Any]:
|
|
1194
|
+
async def set_basic_filter(self, spreadsheetId: str, filter: dict) -> dict[str, Any]:
|
|
1597
1195
|
"""
|
|
1598
1196
|
Sets or updates a basic filter on a specified range within a sheet, enabling data sorting and filtering. The filter's target range and optional sort specifications are defined in a dictionary argument. It is the counterpart to `clear_basic_filter`, which removes an existing filter.
|
|
1599
1197
|
|
|
@@ -1622,27 +1220,19 @@ class GoogleSheetApp(APIApplication):
|
|
|
1622
1220
|
"""
|
|
1623
1221
|
if not spreadsheetId:
|
|
1624
1222
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1625
|
-
|
|
1626
1223
|
if not filter:
|
|
1627
1224
|
raise ValueError("filter cannot be empty")
|
|
1628
|
-
|
|
1629
|
-
# Validate required filter fields
|
|
1630
1225
|
if "range" not in filter:
|
|
1631
1226
|
raise ValueError("filter must contain 'range' field")
|
|
1632
|
-
|
|
1633
|
-
# Validate required filter fields using Google API naming convention
|
|
1634
1227
|
range_data = filter["range"]
|
|
1635
1228
|
if "sheetId" not in range_data:
|
|
1636
1229
|
raise ValueError("filter range must contain 'sheetId' field")
|
|
1637
|
-
|
|
1638
1230
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
1639
|
-
|
|
1640
1231
|
request_body = {"requests": [{"setBasicFilter": {"filter": filter}}]}
|
|
1641
|
-
|
|
1642
1232
|
response = self._post(url, data=request_body)
|
|
1643
1233
|
return self._handle_response(response)
|
|
1644
1234
|
|
|
1645
|
-
def format_cells(
|
|
1235
|
+
async def format_cells(
|
|
1646
1236
|
self,
|
|
1647
1237
|
spreadsheetId: str,
|
|
1648
1238
|
worksheetId: int,
|
|
@@ -1650,34 +1240,26 @@ class GoogleSheetApp(APIApplication):
|
|
|
1650
1240
|
startColumnIndex: int,
|
|
1651
1241
|
endRowIndex: int,
|
|
1652
1242
|
endColumnIndex: int,
|
|
1653
|
-
# Text formatting
|
|
1654
1243
|
bold: bool | None = None,
|
|
1655
1244
|
italic: bool | None = None,
|
|
1656
1245
|
underline: bool | None = None,
|
|
1657
1246
|
strikethrough: bool | None = None,
|
|
1658
1247
|
fontSize: int | None = None,
|
|
1659
1248
|
fontFamily: str | None = None,
|
|
1660
|
-
# Colors
|
|
1661
1249
|
backgroundRed: float | None = None,
|
|
1662
1250
|
backgroundGreen: float | None = None,
|
|
1663
1251
|
backgroundBlue: float | None = None,
|
|
1664
1252
|
textRed: float | None = None,
|
|
1665
1253
|
textGreen: float | None = None,
|
|
1666
1254
|
textBlue: float | None = None,
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
# Text wrapping
|
|
1671
|
-
wrapStrategy: str
|
|
1672
|
-
| None = None, # "OVERFLOW_CELL", "LEGACY_WRAP", "CLIP", "WRAP"
|
|
1673
|
-
# Number format
|
|
1255
|
+
horizontalAlignment: str | None = None,
|
|
1256
|
+
verticalAlignment: str | None = None,
|
|
1257
|
+
wrapStrategy: str | None = None,
|
|
1674
1258
|
numberFormat: str | None = None,
|
|
1675
|
-
# Borders
|
|
1676
1259
|
borderTop: dict | None = None,
|
|
1677
1260
|
borderBottom: dict | None = None,
|
|
1678
1261
|
borderLeft: dict | None = None,
|
|
1679
1262
|
borderRight: dict | None = None,
|
|
1680
|
-
# Merge cells
|
|
1681
1263
|
mergeCells: bool = False,
|
|
1682
1264
|
) -> dict[str, Any]:
|
|
1683
1265
|
"""
|
|
@@ -1733,25 +1315,14 @@ class GoogleSheetApp(APIApplication):
|
|
|
1733
1315
|
"""
|
|
1734
1316
|
if not spreadsheetId:
|
|
1735
1317
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1736
|
-
|
|
1737
1318
|
if worksheetId < 0:
|
|
1738
1319
|
raise ValueError("worksheetId must be non-negative")
|
|
1739
|
-
|
|
1740
|
-
if (
|
|
1741
|
-
startRowIndex < 0
|
|
1742
|
-
or startColumnIndex < 0
|
|
1743
|
-
or endRowIndex < 0
|
|
1744
|
-
or endColumnIndex < 0
|
|
1745
|
-
):
|
|
1320
|
+
if startRowIndex < 0 or startColumnIndex < 0 or endRowIndex < 0 or (endColumnIndex < 0):
|
|
1746
1321
|
raise ValueError("All indices must be non-negative")
|
|
1747
|
-
|
|
1748
1322
|
if startRowIndex >= endRowIndex:
|
|
1749
1323
|
raise ValueError("endRowIndex must be greater than startRowIndex")
|
|
1750
|
-
|
|
1751
1324
|
if startColumnIndex >= endColumnIndex:
|
|
1752
1325
|
raise ValueError("endColumnIndex must be greater than startColumnIndex")
|
|
1753
|
-
|
|
1754
|
-
# Validate color values if provided
|
|
1755
1326
|
for color_name, color_value in [
|
|
1756
1327
|
("backgroundRed", backgroundRed),
|
|
1757
1328
|
("backgroundGreen", backgroundGreen),
|
|
@@ -1760,37 +1331,18 @@ class GoogleSheetApp(APIApplication):
|
|
|
1760
1331
|
("textGreen", textGreen),
|
|
1761
1332
|
("textBlue", textBlue),
|
|
1762
1333
|
]:
|
|
1763
|
-
if color_value is not None and not 0 <= color_value <= 1:
|
|
1334
|
+
if color_value is not None and (not 0 <= color_value <= 1):
|
|
1764
1335
|
raise ValueError(f"{color_name} must be between 0.0 and 1.0")
|
|
1765
|
-
|
|
1766
1336
|
if fontSize is not None and fontSize <= 0:
|
|
1767
1337
|
raise ValueError("fontSize must be positive")
|
|
1768
|
-
|
|
1769
|
-
if horizontalAlignment and horizontalAlignment not in [
|
|
1770
|
-
"LEFT",
|
|
1771
|
-
"CENTER",
|
|
1772
|
-
"RIGHT",
|
|
1773
|
-
]:
|
|
1338
|
+
if horizontalAlignment and horizontalAlignment not in ["LEFT", "CENTER", "RIGHT"]:
|
|
1774
1339
|
raise ValueError('horizontalAlignment must be "LEFT", "CENTER", or "RIGHT"')
|
|
1775
|
-
|
|
1776
1340
|
if verticalAlignment and verticalAlignment not in ["TOP", "MIDDLE", "BOTTOM"]:
|
|
1777
1341
|
raise ValueError('verticalAlignment must be "TOP", "MIDDLE", or "BOTTOM"')
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
"OVERFLOW_CELL",
|
|
1781
|
-
"LEGACY_WRAP",
|
|
1782
|
-
"CLIP",
|
|
1783
|
-
"WRAP",
|
|
1784
|
-
]:
|
|
1785
|
-
raise ValueError(
|
|
1786
|
-
'wrapStrategy must be "OVERFLOW_CELL", "LEGACY_WRAP", "CLIP", or "WRAP"'
|
|
1787
|
-
)
|
|
1788
|
-
|
|
1342
|
+
if wrapStrategy and wrapStrategy not in ["OVERFLOW_CELL", "LEGACY_WRAP", "CLIP", "WRAP"]:
|
|
1343
|
+
raise ValueError('wrapStrategy must be "OVERFLOW_CELL", "LEGACY_WRAP", "CLIP", or "WRAP"')
|
|
1789
1344
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
1790
|
-
|
|
1791
1345
|
requests = []
|
|
1792
|
-
|
|
1793
|
-
# Handle cell merging first if requested
|
|
1794
1346
|
if mergeCells:
|
|
1795
1347
|
requests.append(
|
|
1796
1348
|
{
|
|
@@ -1806,11 +1358,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
1806
1358
|
}
|
|
1807
1359
|
}
|
|
1808
1360
|
)
|
|
1809
|
-
|
|
1810
|
-
# Build the cell format request
|
|
1811
1361
|
cell_format = {}
|
|
1812
|
-
|
|
1813
|
-
# Text format
|
|
1814
1362
|
text_format = {}
|
|
1815
1363
|
if bold is not None:
|
|
1816
1364
|
text_format["bold"] = bold
|
|
@@ -1824,9 +1372,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
1824
1372
|
text_format["fontSize"] = fontSize
|
|
1825
1373
|
if fontFamily is not None:
|
|
1826
1374
|
text_format["fontFamily"] = fontFamily
|
|
1827
|
-
|
|
1828
|
-
# Text color
|
|
1829
|
-
if any(color is not None for color in [textRed, textGreen, textBlue]):
|
|
1375
|
+
if any((color is not None for color in [textRed, textGreen, textBlue])):
|
|
1830
1376
|
text_color = {}
|
|
1831
1377
|
if textRed is not None:
|
|
1832
1378
|
text_color["red"] = textRed
|
|
@@ -1836,15 +1382,9 @@ class GoogleSheetApp(APIApplication):
|
|
|
1836
1382
|
text_color["blue"] = textBlue
|
|
1837
1383
|
if text_color:
|
|
1838
1384
|
text_format["foregroundColor"] = {"rgbColor": text_color}
|
|
1839
|
-
|
|
1840
1385
|
if text_format:
|
|
1841
1386
|
cell_format["textFormat"] = text_format
|
|
1842
|
-
|
|
1843
|
-
# Background color
|
|
1844
|
-
if any(
|
|
1845
|
-
color is not None
|
|
1846
|
-
for color in [backgroundRed, backgroundGreen, backgroundBlue]
|
|
1847
|
-
):
|
|
1387
|
+
if any((color is not None for color in [backgroundRed, backgroundGreen, backgroundBlue])):
|
|
1848
1388
|
background_color = {}
|
|
1849
1389
|
if backgroundRed is not None:
|
|
1850
1390
|
background_color["red"] = backgroundRed
|
|
@@ -1854,35 +1394,19 @@ class GoogleSheetApp(APIApplication):
|
|
|
1854
1394
|
background_color["blue"] = backgroundBlue
|
|
1855
1395
|
if background_color:
|
|
1856
1396
|
cell_format["backgroundColorStyle"] = {"rgbColor": background_color}
|
|
1857
|
-
|
|
1858
|
-
# Alignment
|
|
1859
1397
|
if horizontalAlignment or verticalAlignment:
|
|
1860
1398
|
cell_format["horizontalAlignment"] = horizontalAlignment
|
|
1861
1399
|
cell_format["verticalAlignment"] = verticalAlignment
|
|
1862
|
-
|
|
1863
|
-
# Text wrapping
|
|
1864
1400
|
if wrapStrategy:
|
|
1865
1401
|
cell_format["wrapStrategy"] = wrapStrategy
|
|
1866
|
-
|
|
1867
|
-
# Number format
|
|
1868
1402
|
if numberFormat:
|
|
1869
1403
|
cell_format["numberFormat"] = {"type": "TEXT", "pattern": numberFormat}
|
|
1870
|
-
|
|
1871
|
-
# Borders
|
|
1872
1404
|
borders = {}
|
|
1873
|
-
for border_side, border_config in [
|
|
1874
|
-
("top", borderTop),
|
|
1875
|
-
("bottom", borderBottom),
|
|
1876
|
-
("left", borderLeft),
|
|
1877
|
-
("right", borderRight),
|
|
1878
|
-
]:
|
|
1405
|
+
for border_side, border_config in [("top", borderTop), ("bottom", borderBottom), ("left", borderLeft), ("right", borderRight)]:
|
|
1879
1406
|
if border_config:
|
|
1880
1407
|
borders[border_side] = border_config
|
|
1881
|
-
|
|
1882
1408
|
if borders:
|
|
1883
1409
|
cell_format["borders"] = borders
|
|
1884
|
-
|
|
1885
|
-
# Add cell formatting request if any formatting is specified
|
|
1886
1410
|
if cell_format:
|
|
1887
1411
|
requests.append(
|
|
1888
1412
|
{
|
|
@@ -1899,7 +1423,6 @@ class GoogleSheetApp(APIApplication):
|
|
|
1899
1423
|
}
|
|
1900
1424
|
}
|
|
1901
1425
|
)
|
|
1902
|
-
|
|
1903
1426
|
request_body = {"requests": requests}
|
|
1904
1427
|
response = self._post(url, data=request_body)
|
|
1905
1428
|
return self._handle_response(response)
|