universal-mcp-applications 0.1.32__py3-none-any.whl → 0.1.36rc2__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.
- 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 +36 -103
- 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 +24 -101
- 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 +85 -189
- universal_mcp/applications/google_drive/app.py +141 -463
- 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 +100 -581
- 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 +9 -2
- universal_mcp/applications/linkedin/app.py +240 -181
- 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 +16 -38
- universal_mcp/applications/openai/app.py +42 -165
- universal_mcp/applications/outlook/app.py +24 -84
- 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 +50 -109
- 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 +17 -39
- universal_mcp/applications/shopify/app.py +1551 -4287
- universal_mcp/applications/shortcut/app.py +155 -417
- universal_mcp/applications/slack/app.py +33 -115
- 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.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/METADATA +2 -2
- {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/RECORD +105 -105
- {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.32.dist-info → universal_mcp_applications-0.1.36rc2.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,7 +14,7 @@ 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
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
|
|
|
@@ -40,12 +35,10 @@ class GoogleSheetApp(APIApplication):
|
|
|
40
35
|
spreadsheet_data = {"properties": {"title": title}}
|
|
41
36
|
response = self._post(url, data=spreadsheet_data)
|
|
42
37
|
payload = self._handle_response(response)
|
|
43
|
-
payload["Note"] = (
|
|
44
|
-
"You must load and call other google_sheet content functions (like `google_sheet__write_values_to_sheet`)"
|
|
45
|
-
)
|
|
38
|
+
payload["Note"] = "You must load and call other google_sheet content functions (like `google_sheet__write_values_to_sheet`)"
|
|
46
39
|
return payload
|
|
47
40
|
|
|
48
|
-
def get_spreadsheet_metadata(self, spreadsheetId: str) -> dict[str, Any]:
|
|
41
|
+
async def get_spreadsheet_metadata(self, spreadsheetId: str) -> dict[str, Any]:
|
|
49
42
|
"""
|
|
50
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.
|
|
51
44
|
|
|
@@ -67,7 +60,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
67
60
|
response = self._get(url)
|
|
68
61
|
return self._handle_response(response)
|
|
69
62
|
|
|
70
|
-
def get_values(
|
|
63
|
+
async def get_values(
|
|
71
64
|
self,
|
|
72
65
|
spreadsheetId: str,
|
|
73
66
|
range: str,
|
|
@@ -97,20 +90,16 @@ class GoogleSheetApp(APIApplication):
|
|
|
97
90
|
"""
|
|
98
91
|
url = f"{self.base_url}/{spreadsheetId}/values/{range}"
|
|
99
92
|
params = {}
|
|
100
|
-
|
|
101
93
|
if majorDimension:
|
|
102
94
|
params["majorDimension"] = majorDimension
|
|
103
95
|
if valueRenderOption:
|
|
104
96
|
params["valueRenderOption"] = valueRenderOption
|
|
105
97
|
if dateTimeRenderOption:
|
|
106
98
|
params["dateTimeRenderOption"] = dateTimeRenderOption
|
|
107
|
-
|
|
108
99
|
response = self._get(url, params=params)
|
|
109
100
|
return self._handle_response(response)
|
|
110
101
|
|
|
111
|
-
def batch_get_values_by_range(
|
|
112
|
-
self, spreadsheetId: str, ranges: list[str] | None = None
|
|
113
|
-
) -> dict[str, Any]:
|
|
102
|
+
async def batch_get_values_by_range(self, spreadsheetId: str, ranges: list[str] | None = None) -> dict[str, Any]:
|
|
114
103
|
"""
|
|
115
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.
|
|
116
105
|
|
|
@@ -135,7 +124,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
135
124
|
response = self._get(url, params=params)
|
|
136
125
|
return self._handle_response(response)
|
|
137
126
|
|
|
138
|
-
def insert_dimensions(
|
|
127
|
+
async def insert_dimensions(
|
|
139
128
|
self,
|
|
140
129
|
spreadsheetId: str,
|
|
141
130
|
sheet_id: int,
|
|
@@ -176,56 +165,33 @@ class GoogleSheetApp(APIApplication):
|
|
|
176
165
|
"""
|
|
177
166
|
if not spreadsheetId:
|
|
178
167
|
raise ValueError("spreadsheetId cannot be empty")
|
|
179
|
-
|
|
180
168
|
if dimension not in ["ROWS", "COLUMNS"]:
|
|
181
169
|
raise ValueError('dimension must be either "ROWS" or "COLUMNS"')
|
|
182
|
-
|
|
183
170
|
if start_index < 0 or end_index < 0:
|
|
184
171
|
raise ValueError("start_index and end_index must be non-negative")
|
|
185
|
-
|
|
186
172
|
if start_index >= end_index:
|
|
187
173
|
raise ValueError("end_index must be greater than start_index")
|
|
188
|
-
|
|
189
174
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
190
|
-
|
|
191
175
|
request_body: dict[str, Any] = {
|
|
192
176
|
"requests": [
|
|
193
177
|
{
|
|
194
178
|
"insertDimension": {
|
|
195
179
|
"inheritFromBefore": inherit_from_before,
|
|
196
|
-
"range": {
|
|
197
|
-
"dimension": dimension,
|
|
198
|
-
"sheetId": sheet_id,
|
|
199
|
-
"startIndex": start_index,
|
|
200
|
-
"endIndex": end_index,
|
|
201
|
-
},
|
|
180
|
+
"range": {"dimension": dimension, "sheetId": sheet_id, "startIndex": start_index, "endIndex": end_index},
|
|
202
181
|
}
|
|
203
182
|
}
|
|
204
183
|
]
|
|
205
184
|
}
|
|
206
|
-
|
|
207
|
-
# Add optional parameters if provided
|
|
208
185
|
if include_spreadsheet_in_response is not None:
|
|
209
|
-
request_body["includeSpreadsheetInResponse"] =
|
|
210
|
-
include_spreadsheet_in_response
|
|
211
|
-
)
|
|
212
|
-
|
|
186
|
+
request_body["includeSpreadsheetInResponse"] = include_spreadsheet_in_response
|
|
213
187
|
if response_include_grid_data is not None:
|
|
214
188
|
request_body["responseIncludeGridData"] = response_include_grid_data
|
|
215
|
-
|
|
216
189
|
if response_ranges is not None:
|
|
217
190
|
request_body["responseRanges"] = response_ranges
|
|
218
|
-
|
|
219
191
|
response = self._post(url, data=request_body)
|
|
220
192
|
return self._handle_response(response)
|
|
221
193
|
|
|
222
|
-
def append_dimensions(
|
|
223
|
-
self,
|
|
224
|
-
spreadsheetId: str,
|
|
225
|
-
sheet_id: int,
|
|
226
|
-
dimension: str,
|
|
227
|
-
length: int,
|
|
228
|
-
) -> dict[str, Any]:
|
|
194
|
+
async def append_dimensions(self, spreadsheetId: str, sheet_id: int, dimension: str, length: int) -> dict[str, Any]:
|
|
229
195
|
"""
|
|
230
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.
|
|
231
197
|
|
|
@@ -250,31 +216,16 @@ class GoogleSheetApp(APIApplication):
|
|
|
250
216
|
"""
|
|
251
217
|
if not spreadsheetId:
|
|
252
218
|
raise ValueError("spreadsheetId cannot be empty")
|
|
253
|
-
|
|
254
219
|
if dimension not in ["ROWS", "COLUMNS"]:
|
|
255
220
|
raise ValueError('dimension must be either "ROWS" or "COLUMNS"')
|
|
256
|
-
|
|
257
221
|
if length <= 0:
|
|
258
222
|
raise ValueError("length must be a positive integer")
|
|
259
|
-
|
|
260
223
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
261
|
-
|
|
262
|
-
request_body = {
|
|
263
|
-
"requests": [
|
|
264
|
-
{
|
|
265
|
-
"appendDimension": {
|
|
266
|
-
"sheetId": sheet_id,
|
|
267
|
-
"dimension": dimension,
|
|
268
|
-
"length": length,
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
]
|
|
272
|
-
}
|
|
273
|
-
|
|
224
|
+
request_body = {"requests": [{"appendDimension": {"sheetId": sheet_id, "dimension": dimension, "length": length}}]}
|
|
274
225
|
response = self._post(url, data=request_body)
|
|
275
226
|
return self._handle_response(response)
|
|
276
227
|
|
|
277
|
-
def delete_dimensions(
|
|
228
|
+
async def delete_dimensions(
|
|
278
229
|
self,
|
|
279
230
|
spreadsheetId: str,
|
|
280
231
|
sheet_id: int,
|
|
@@ -310,49 +261,32 @@ class GoogleSheetApp(APIApplication):
|
|
|
310
261
|
"""
|
|
311
262
|
if not spreadsheetId:
|
|
312
263
|
raise ValueError("spreadsheetId cannot be empty")
|
|
313
|
-
|
|
314
264
|
if dimension not in ["ROWS", "COLUMNS"]:
|
|
315
265
|
raise ValueError('dimension must be either "ROWS" or "COLUMNS"')
|
|
316
|
-
|
|
317
266
|
if start_index < 0 or end_index < 0:
|
|
318
267
|
raise ValueError("start_index and end_index must be non-negative")
|
|
319
|
-
|
|
320
268
|
if start_index >= end_index:
|
|
321
269
|
raise ValueError("end_index must be greater than start_index")
|
|
322
|
-
|
|
323
270
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
324
|
-
|
|
325
271
|
request_body: dict[str, Any] = {
|
|
326
272
|
"requests": [
|
|
327
273
|
{
|
|
328
274
|
"deleteDimension": {
|
|
329
|
-
"range": {
|
|
330
|
-
"sheetId": sheet_id,
|
|
331
|
-
"dimension": dimension,
|
|
332
|
-
"startIndex": start_index,
|
|
333
|
-
"endIndex": end_index,
|
|
334
|
-
}
|
|
275
|
+
"range": {"sheetId": sheet_id, "dimension": dimension, "startIndex": start_index, "endIndex": end_index}
|
|
335
276
|
}
|
|
336
277
|
}
|
|
337
278
|
]
|
|
338
279
|
}
|
|
339
|
-
|
|
340
|
-
# Add optional response parameters if provided
|
|
341
280
|
if include_spreadsheet_in_response is not None:
|
|
342
|
-
request_body["includeSpreadsheetInResponse"] =
|
|
343
|
-
include_spreadsheet_in_response
|
|
344
|
-
)
|
|
345
|
-
|
|
281
|
+
request_body["includeSpreadsheetInResponse"] = include_spreadsheet_in_response
|
|
346
282
|
if response_include_grid_data is not None:
|
|
347
283
|
request_body["responseIncludeGridData"] = response_include_grid_data
|
|
348
|
-
|
|
349
284
|
if response_ranges is not None:
|
|
350
285
|
request_body["responseRanges"] = response_ranges
|
|
351
|
-
|
|
352
286
|
response = self._post(url, data=request_body)
|
|
353
287
|
return self._handle_response(response)
|
|
354
288
|
|
|
355
|
-
def add_sheet(
|
|
289
|
+
async def add_sheet(
|
|
356
290
|
self,
|
|
357
291
|
spreadsheetId: str,
|
|
358
292
|
title: str | None = None,
|
|
@@ -362,7 +296,6 @@ class GoogleSheetApp(APIApplication):
|
|
|
362
296
|
hidden: bool | None = None,
|
|
363
297
|
rightToLeft: bool | None = None,
|
|
364
298
|
tabColorStyle: dict | None = None,
|
|
365
|
-
# Grid properties
|
|
366
299
|
rowCount: int | None = None,
|
|
367
300
|
columnCount: int | None = None,
|
|
368
301
|
frozenRowCount: int | None = None,
|
|
@@ -370,7 +303,6 @@ class GoogleSheetApp(APIApplication):
|
|
|
370
303
|
hideGridlines: bool | None = None,
|
|
371
304
|
rowGroupControlAfter: bool | None = None,
|
|
372
305
|
columnGroupControlAfter: bool | None = None,
|
|
373
|
-
# Response options
|
|
374
306
|
includeSpreadsheetInResponse: bool = False,
|
|
375
307
|
responseIncludeGridData: bool = False,
|
|
376
308
|
) -> dict[str, Any]:
|
|
@@ -408,80 +340,61 @@ class GoogleSheetApp(APIApplication):
|
|
|
408
340
|
"""
|
|
409
341
|
if not spreadsheetId:
|
|
410
342
|
raise ValueError("spreadsheetId cannot be empty")
|
|
411
|
-
|
|
412
343
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
413
|
-
|
|
414
|
-
# Build the addSheet request with properties
|
|
415
344
|
add_sheet_request = {"properties": {}}
|
|
416
|
-
|
|
417
345
|
if title is not None:
|
|
418
346
|
add_sheet_request["properties"]["title"] = title
|
|
419
|
-
|
|
420
347
|
if sheetId is not None:
|
|
421
348
|
add_sheet_request["properties"]["sheetId"] = sheetId
|
|
422
|
-
|
|
423
349
|
if index is not None:
|
|
424
350
|
add_sheet_request["properties"]["index"] = index
|
|
425
|
-
|
|
426
351
|
if sheetType is not None:
|
|
427
352
|
add_sheet_request["properties"]["sheetType"] = sheetType
|
|
428
|
-
|
|
429
353
|
if hidden is not None:
|
|
430
354
|
add_sheet_request["properties"]["hidden"] = hidden
|
|
431
|
-
|
|
432
355
|
if rightToLeft is not None:
|
|
433
356
|
add_sheet_request["properties"]["rightToLeft"] = rightToLeft
|
|
434
|
-
|
|
435
357
|
if tabColorStyle is not None:
|
|
436
358
|
add_sheet_request["properties"]["tabColorStyle"] = tabColorStyle
|
|
437
|
-
|
|
438
|
-
# Build grid properties if any grid-related parameters are provided
|
|
439
359
|
grid_properties = {}
|
|
440
360
|
if any(
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
+
)
|
|
451
373
|
):
|
|
452
374
|
if rowCount is not None:
|
|
453
375
|
grid_properties["rowCount"] = rowCount
|
|
454
|
-
|
|
455
376
|
if columnCount is not None:
|
|
456
377
|
grid_properties["columnCount"] = columnCount
|
|
457
|
-
|
|
458
378
|
if frozenRowCount is not None:
|
|
459
379
|
grid_properties["frozenRowCount"] = frozenRowCount
|
|
460
|
-
|
|
461
380
|
if frozenColumnCount is not None:
|
|
462
381
|
grid_properties["frozenColumnCount"] = frozenColumnCount
|
|
463
|
-
|
|
464
382
|
if hideGridlines is not None:
|
|
465
383
|
grid_properties["hideGridlines"] = hideGridlines
|
|
466
|
-
|
|
467
384
|
if rowGroupControlAfter is not None:
|
|
468
385
|
grid_properties["rowGroupControlAfter"] = rowGroupControlAfter
|
|
469
|
-
|
|
470
386
|
if columnGroupControlAfter is not None:
|
|
471
387
|
grid_properties["columnGroupControlAfter"] = columnGroupControlAfter
|
|
472
|
-
|
|
473
388
|
add_sheet_request["properties"]["gridProperties"] = grid_properties
|
|
474
|
-
|
|
475
389
|
request_body = {
|
|
476
390
|
"requests": [{"addSheet": add_sheet_request}],
|
|
477
391
|
"includeSpreadsheetInResponse": includeSpreadsheetInResponse,
|
|
478
392
|
"responseIncludeGridData": responseIncludeGridData,
|
|
479
393
|
}
|
|
480
|
-
|
|
481
394
|
response = self._post(url, data=request_body)
|
|
482
395
|
return self._handle_response(response)
|
|
483
396
|
|
|
484
|
-
def add_basic_chart(
|
|
397
|
+
async def add_basic_chart(
|
|
485
398
|
self,
|
|
486
399
|
spreadsheetId: str,
|
|
487
400
|
source_sheet_id: int,
|
|
@@ -524,27 +437,17 @@ class GoogleSheetApp(APIApplication):
|
|
|
524
437
|
"""
|
|
525
438
|
if not spreadsheetId:
|
|
526
439
|
raise ValueError("spreadsheetId cannot be empty")
|
|
527
|
-
|
|
528
440
|
if not chart_title:
|
|
529
441
|
raise ValueError("chart_title cannot be empty")
|
|
530
|
-
|
|
531
442
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
532
|
-
|
|
533
|
-
# Build the chart specification
|
|
534
443
|
chart_spec = {
|
|
535
444
|
"title": chart_title,
|
|
536
445
|
"basicChart": {
|
|
537
446
|
"chartType": chart_type,
|
|
538
447
|
"legendPosition": "BOTTOM_LEGEND",
|
|
539
448
|
"axis": [
|
|
540
|
-
{
|
|
541
|
-
|
|
542
|
-
"title": x_axis_title if x_axis_title else "Categories",
|
|
543
|
-
},
|
|
544
|
-
{
|
|
545
|
-
"position": "LEFT_AXIS",
|
|
546
|
-
"title": y_axis_title if y_axis_title else "Values",
|
|
547
|
-
},
|
|
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"},
|
|
548
451
|
],
|
|
549
452
|
"domains": [
|
|
550
453
|
{
|
|
@@ -553,18 +456,10 @@ class GoogleSheetApp(APIApplication):
|
|
|
553
456
|
"sources": [
|
|
554
457
|
{
|
|
555
458
|
"sheetId": source_sheet_id,
|
|
556
|
-
"startRowIndex": domain_range.get(
|
|
557
|
-
|
|
558
|
-
),
|
|
559
|
-
"
|
|
560
|
-
"endRowIndex", 1
|
|
561
|
-
),
|
|
562
|
-
"startColumnIndex": domain_range.get(
|
|
563
|
-
"startColumnIndex", 0
|
|
564
|
-
),
|
|
565
|
-
"endColumnIndex": domain_range.get(
|
|
566
|
-
"endColumnIndex", 1
|
|
567
|
-
),
|
|
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),
|
|
568
463
|
}
|
|
569
464
|
]
|
|
570
465
|
}
|
|
@@ -575,8 +470,6 @@ class GoogleSheetApp(APIApplication):
|
|
|
575
470
|
"headerCount": 1,
|
|
576
471
|
},
|
|
577
472
|
}
|
|
578
|
-
|
|
579
|
-
# Add series data
|
|
580
473
|
for series_range in series_ranges:
|
|
581
474
|
series = {
|
|
582
475
|
"series": {
|
|
@@ -586,9 +479,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
586
479
|
"sheetId": source_sheet_id,
|
|
587
480
|
"startRowIndex": series_range.get("startRowIndex", 0),
|
|
588
481
|
"endRowIndex": series_range.get("endRowIndex", 1),
|
|
589
|
-
"startColumnIndex": series_range.get(
|
|
590
|
-
"startColumnIndex", 0
|
|
591
|
-
),
|
|
482
|
+
"startColumnIndex": series_range.get("startColumnIndex", 0),
|
|
592
483
|
"endColumnIndex": series_range.get("endColumnIndex", 1),
|
|
593
484
|
}
|
|
594
485
|
]
|
|
@@ -597,40 +488,25 @@ class GoogleSheetApp(APIApplication):
|
|
|
597
488
|
"targetAxis": "LEFT_AXIS",
|
|
598
489
|
}
|
|
599
490
|
chart_spec["basicChart"]["series"].append(series)
|
|
600
|
-
|
|
601
|
-
# Build the position specification
|
|
602
491
|
if new_sheet:
|
|
603
492
|
position_spec = {"newSheet": True}
|
|
604
|
-
# For existing sheet, use overlayPosition structure
|
|
605
493
|
elif chart_position:
|
|
606
494
|
position_spec = chart_position
|
|
607
495
|
else:
|
|
608
|
-
# Default positioning when placing in existing sheet
|
|
609
496
|
position_spec = {
|
|
610
497
|
"overlayPosition": {
|
|
611
|
-
"anchorCell": {
|
|
612
|
-
"sheetId": source_sheet_id,
|
|
613
|
-
"rowIndex": 0,
|
|
614
|
-
"columnIndex": 0,
|
|
615
|
-
},
|
|
498
|
+
"anchorCell": {"sheetId": source_sheet_id, "rowIndex": 0, "columnIndex": 0},
|
|
616
499
|
"offsetXPixels": 0,
|
|
617
500
|
"offsetYPixels": 0,
|
|
618
501
|
"widthPixels": 600,
|
|
619
502
|
"heightPixels": 400,
|
|
620
503
|
}
|
|
621
504
|
}
|
|
622
|
-
|
|
623
|
-
# Build the request body
|
|
624
|
-
request_body = {
|
|
625
|
-
"requests": [
|
|
626
|
-
{"addChart": {"chart": {"spec": chart_spec, "position": position_spec}}}
|
|
627
|
-
]
|
|
628
|
-
}
|
|
629
|
-
|
|
505
|
+
request_body = {"requests": [{"addChart": {"chart": {"spec": chart_spec, "position": position_spec}}}]}
|
|
630
506
|
response = self._post(url, data=request_body)
|
|
631
507
|
return self._handle_response(response)
|
|
632
508
|
|
|
633
|
-
def add_pie_chart(
|
|
509
|
+
async def add_pie_chart(
|
|
634
510
|
self,
|
|
635
511
|
spreadsheetId: str,
|
|
636
512
|
source_sheet_id: int,
|
|
@@ -669,16 +545,11 @@ class GoogleSheetApp(APIApplication):
|
|
|
669
545
|
"""
|
|
670
546
|
if not spreadsheetId:
|
|
671
547
|
raise ValueError("spreadsheetId cannot be empty")
|
|
672
|
-
|
|
673
548
|
if not chart_title:
|
|
674
549
|
raise ValueError("chart_title cannot be empty")
|
|
675
|
-
|
|
676
|
-
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):
|
|
677
551
|
raise ValueError("pie_hole must be between 0.0 and 1.0")
|
|
678
|
-
|
|
679
552
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
680
|
-
|
|
681
|
-
# Build the pie chart specification
|
|
682
553
|
pie_chart_spec = {
|
|
683
554
|
"legendPosition": legend_position,
|
|
684
555
|
"domain": {
|
|
@@ -701,55 +572,35 @@ class GoogleSheetApp(APIApplication):
|
|
|
701
572
|
"sheetId": source_sheet_id,
|
|
702
573
|
"startRowIndex": data_range.get("startRowIndex", 0),
|
|
703
574
|
"endRowIndex": data_range.get("endRowIndex", 1),
|
|
704
|
-
"startColumnIndex": data_range.get("startColumnIndex", 0)
|
|
705
|
-
+ 1,
|
|
575
|
+
"startColumnIndex": data_range.get("startColumnIndex", 0) + 1,
|
|
706
576
|
"endColumnIndex": data_range.get("endColumnIndex", 2),
|
|
707
577
|
}
|
|
708
578
|
]
|
|
709
579
|
}
|
|
710
580
|
},
|
|
711
581
|
}
|
|
712
|
-
|
|
713
|
-
# Add pie hole for donut chart if specified
|
|
714
582
|
if pie_hole is not None:
|
|
715
583
|
pie_chart_spec["pieHole"] = pie_hole
|
|
716
|
-
|
|
717
|
-
# Build the chart specification
|
|
718
584
|
chart_spec = {"title": chart_title, "pieChart": pie_chart_spec}
|
|
719
|
-
|
|
720
|
-
# Build the position specification
|
|
721
585
|
if new_sheet:
|
|
722
586
|
position_spec = {"newSheet": True}
|
|
723
|
-
# For existing sheet, use overlayPosition structure
|
|
724
587
|
elif chart_position:
|
|
725
588
|
position_spec = chart_position
|
|
726
589
|
else:
|
|
727
|
-
# Default positioning when placing in existing sheet
|
|
728
590
|
position_spec = {
|
|
729
591
|
"overlayPosition": {
|
|
730
|
-
"anchorCell": {
|
|
731
|
-
"sheetId": source_sheet_id,
|
|
732
|
-
"rowIndex": 0,
|
|
733
|
-
"columnIndex": 0,
|
|
734
|
-
},
|
|
592
|
+
"anchorCell": {"sheetId": source_sheet_id, "rowIndex": 0, "columnIndex": 0},
|
|
735
593
|
"offsetXPixels": 0,
|
|
736
594
|
"offsetYPixels": 0,
|
|
737
595
|
"widthPixels": 600,
|
|
738
596
|
"heightPixels": 400,
|
|
739
597
|
}
|
|
740
598
|
}
|
|
741
|
-
|
|
742
|
-
# Build the request body
|
|
743
|
-
request_body = {
|
|
744
|
-
"requests": [
|
|
745
|
-
{"addChart": {"chart": {"spec": chart_spec, "position": position_spec}}}
|
|
746
|
-
]
|
|
747
|
-
}
|
|
748
|
-
|
|
599
|
+
request_body = {"requests": [{"addChart": {"chart": {"spec": chart_spec, "position": position_spec}}}]}
|
|
749
600
|
response = self._post(url, data=request_body)
|
|
750
601
|
return self._handle_response(response)
|
|
751
602
|
|
|
752
|
-
def add_table(
|
|
603
|
+
async def add_table(
|
|
753
604
|
self,
|
|
754
605
|
spreadsheetId: str,
|
|
755
606
|
sheet_id: int,
|
|
@@ -789,51 +640,24 @@ class GoogleSheetApp(APIApplication):
|
|
|
789
640
|
"""
|
|
790
641
|
if not spreadsheetId:
|
|
791
642
|
raise ValueError("spreadsheetId cannot be empty")
|
|
792
|
-
|
|
793
643
|
if not table_name:
|
|
794
644
|
raise ValueError("table_name cannot be empty")
|
|
795
|
-
|
|
796
645
|
if not table_id:
|
|
797
646
|
raise ValueError("table_id cannot be empty")
|
|
798
|
-
|
|
799
|
-
if (
|
|
800
|
-
start_row_index < 0
|
|
801
|
-
or end_row_index < 0
|
|
802
|
-
or start_column_index < 0
|
|
803
|
-
or end_column_index < 0
|
|
804
|
-
):
|
|
647
|
+
if start_row_index < 0 or end_row_index < 0 or start_column_index < 0 or (end_column_index < 0):
|
|
805
648
|
raise ValueError("All indices must be non-negative")
|
|
806
|
-
|
|
807
649
|
if start_row_index >= end_row_index:
|
|
808
650
|
raise ValueError("end_row_index must be greater than start_row_index")
|
|
809
|
-
|
|
810
651
|
if start_column_index >= end_column_index:
|
|
811
652
|
raise ValueError("end_column_index must be greater than start_column_index")
|
|
812
|
-
|
|
813
|
-
# Validate column properties if provided
|
|
814
653
|
if column_properties:
|
|
815
|
-
valid_column_types = [
|
|
816
|
-
"TEXT",
|
|
817
|
-
"PERCENT",
|
|
818
|
-
"DROPDOWN",
|
|
819
|
-
"DOUBLE",
|
|
820
|
-
"CURRENCY",
|
|
821
|
-
"DATE",
|
|
822
|
-
"TIME",
|
|
823
|
-
"DATE_TIME",
|
|
824
|
-
]
|
|
654
|
+
valid_column_types = ["TEXT", "PERCENT", "DROPDOWN", "DOUBLE", "CURRENCY", "DATE", "TIME", "DATE_TIME"]
|
|
825
655
|
for i, prop in enumerate(column_properties):
|
|
826
|
-
if
|
|
827
|
-
"columnType" in prop
|
|
828
|
-
and prop["columnType"] not in valid_column_types
|
|
829
|
-
):
|
|
656
|
+
if "columnType" in prop and prop["columnType"] not in valid_column_types:
|
|
830
657
|
raise ValueError(
|
|
831
658
|
f"Invalid column type '{prop['columnType']}' at index {i}. Valid types are: {', '.join(valid_column_types)}"
|
|
832
659
|
)
|
|
833
|
-
|
|
834
660
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
835
|
-
|
|
836
|
-
# Build the table specification
|
|
837
661
|
table_spec = {
|
|
838
662
|
"name": table_name,
|
|
839
663
|
"tableId": table_id,
|
|
@@ -845,18 +669,13 @@ class GoogleSheetApp(APIApplication):
|
|
|
845
669
|
"endRowIndex": end_row_index,
|
|
846
670
|
},
|
|
847
671
|
}
|
|
848
|
-
|
|
849
|
-
# Add column properties if provided
|
|
850
672
|
if column_properties:
|
|
851
673
|
table_spec["columnProperties"] = column_properties
|
|
852
|
-
|
|
853
|
-
# Build the request body
|
|
854
674
|
request_body = {"requests": [{"addTable": {"table": table_spec}}]}
|
|
855
|
-
|
|
856
675
|
response = self._post(url, data=request_body)
|
|
857
676
|
return self._handle_response(response)
|
|
858
677
|
|
|
859
|
-
def update_table(
|
|
678
|
+
async def update_table(
|
|
860
679
|
self,
|
|
861
680
|
spreadsheetId: str,
|
|
862
681
|
table_id: str,
|
|
@@ -895,70 +714,33 @@ class GoogleSheetApp(APIApplication):
|
|
|
895
714
|
"""
|
|
896
715
|
if not spreadsheetId:
|
|
897
716
|
raise ValueError("spreadsheetId cannot be empty")
|
|
898
|
-
|
|
899
717
|
if not table_id:
|
|
900
718
|
raise ValueError("table_id cannot be empty")
|
|
901
|
-
|
|
902
|
-
# Validate indices if provided
|
|
903
719
|
if start_row_index is not None and start_row_index < 0:
|
|
904
720
|
raise ValueError("start_row_index must be non-negative")
|
|
905
|
-
|
|
906
721
|
if end_row_index is not None and end_row_index < 0:
|
|
907
722
|
raise ValueError("end_row_index must be non-negative")
|
|
908
|
-
|
|
909
723
|
if start_column_index is not None and start_column_index < 0:
|
|
910
724
|
raise ValueError("start_column_index must be non-negative")
|
|
911
|
-
|
|
912
725
|
if end_column_index is not None and end_column_index < 0:
|
|
913
726
|
raise ValueError("end_column_index must be non-negative")
|
|
914
|
-
|
|
915
|
-
if (
|
|
916
|
-
start_row_index is not None
|
|
917
|
-
and end_row_index is not None
|
|
918
|
-
and start_row_index >= end_row_index
|
|
919
|
-
):
|
|
727
|
+
if start_row_index is not None and end_row_index is not None and (start_row_index >= end_row_index):
|
|
920
728
|
raise ValueError("end_row_index must be greater than start_row_index")
|
|
921
|
-
|
|
922
|
-
if (
|
|
923
|
-
start_column_index is not None
|
|
924
|
-
and end_column_index is not None
|
|
925
|
-
and start_column_index >= end_column_index
|
|
926
|
-
):
|
|
729
|
+
if start_column_index is not None and end_column_index is not None and (start_column_index >= end_column_index):
|
|
927
730
|
raise ValueError("end_column_index must be greater than start_column_index")
|
|
928
|
-
|
|
929
|
-
# Validate column properties if provided
|
|
930
731
|
if column_properties:
|
|
931
|
-
valid_column_types = [
|
|
932
|
-
"TEXT",
|
|
933
|
-
"PERCENT",
|
|
934
|
-
"DROPDOWN",
|
|
935
|
-
"DOUBLE",
|
|
936
|
-
"CURRENCY",
|
|
937
|
-
"DATE",
|
|
938
|
-
"TIME",
|
|
939
|
-
"DATE_TIME",
|
|
940
|
-
]
|
|
732
|
+
valid_column_types = ["TEXT", "PERCENT", "DROPDOWN", "DOUBLE", "CURRENCY", "DATE", "TIME", "DATE_TIME"]
|
|
941
733
|
for i, prop in enumerate(column_properties):
|
|
942
|
-
if
|
|
943
|
-
"columnType" in prop
|
|
944
|
-
and prop["columnType"] not in valid_column_types
|
|
945
|
-
):
|
|
734
|
+
if "columnType" in prop and prop["columnType"] not in valid_column_types:
|
|
946
735
|
raise ValueError(
|
|
947
736
|
f"Invalid column type '{prop['columnType']}' at index {i}. Valid types are: {', '.join(valid_column_types)}"
|
|
948
737
|
)
|
|
949
|
-
|
|
950
738
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
951
|
-
|
|
952
|
-
# Build the table specification and track fields to update
|
|
953
739
|
table_spec: dict[str, Any] = {"tableId": table_id}
|
|
954
740
|
fields_to_update = []
|
|
955
|
-
|
|
956
|
-
# Add optional properties if provided
|
|
957
741
|
if table_name is not None:
|
|
958
742
|
table_spec["name"] = table_name
|
|
959
743
|
fields_to_update.append("name")
|
|
960
|
-
|
|
961
|
-
# Build range if any range parameters are provided
|
|
962
744
|
range_params: dict[str, Any] = {}
|
|
963
745
|
if start_row_index is not None:
|
|
964
746
|
range_params["startRowIndex"] = start_row_index
|
|
@@ -968,38 +750,19 @@ class GoogleSheetApp(APIApplication):
|
|
|
968
750
|
range_params["startColumnIndex"] = start_column_index
|
|
969
751
|
if end_column_index is not None:
|
|
970
752
|
range_params["endColumnIndex"] = end_column_index
|
|
971
|
-
|
|
972
753
|
if range_params:
|
|
973
754
|
table_spec["range"] = range_params
|
|
974
755
|
fields_to_update.append("range")
|
|
975
|
-
|
|
976
|
-
# Add column properties if provided
|
|
977
756
|
if column_properties:
|
|
978
757
|
table_spec["columnProperties"] = column_properties
|
|
979
758
|
fields_to_update.append("columnProperties")
|
|
980
|
-
|
|
981
|
-
# Validate that at least one field is being updated
|
|
982
759
|
if not fields_to_update:
|
|
983
|
-
raise ValueError(
|
|
984
|
-
|
|
985
|
-
)
|
|
986
|
-
|
|
987
|
-
# Build the request body
|
|
988
|
-
request_body = {
|
|
989
|
-
"requests": [
|
|
990
|
-
{
|
|
991
|
-
"updateTable": {
|
|
992
|
-
"table": table_spec,
|
|
993
|
-
"fields": ",".join(fields_to_update),
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
]
|
|
997
|
-
}
|
|
998
|
-
|
|
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)}}]}
|
|
999
762
|
response = self._post(url, data=request_body)
|
|
1000
763
|
return self._handle_response(response)
|
|
1001
764
|
|
|
1002
|
-
def clear_values(self, spreadsheetId: str, range: str) -> dict[str, Any]:
|
|
765
|
+
async def clear_values(self, spreadsheetId: str, range: str) -> dict[str, Any]:
|
|
1003
766
|
"""
|
|
1004
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.
|
|
1005
768
|
|
|
@@ -1021,12 +784,8 @@ class GoogleSheetApp(APIApplication):
|
|
|
1021
784
|
response = self._post(url, data={})
|
|
1022
785
|
return self._handle_response(response)
|
|
1023
786
|
|
|
1024
|
-
def update_values(
|
|
1025
|
-
self,
|
|
1026
|
-
spreadsheetId: str,
|
|
1027
|
-
range: str,
|
|
1028
|
-
values: list[list[Any]],
|
|
1029
|
-
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"
|
|
1030
789
|
) -> dict[str, Any]:
|
|
1031
790
|
"""
|
|
1032
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.
|
|
@@ -1053,11 +812,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
1053
812
|
response = self._put(url, data=data, params=params)
|
|
1054
813
|
return self._handle_response(response)
|
|
1055
814
|
|
|
1056
|
-
def batch_clear_values(
|
|
1057
|
-
self,
|
|
1058
|
-
spreadsheetId: str,
|
|
1059
|
-
ranges: list[str],
|
|
1060
|
-
) -> dict[str, Any]:
|
|
815
|
+
async def batch_clear_values(self, spreadsheetId: str, ranges: list[str]) -> dict[str, Any]:
|
|
1061
816
|
"""
|
|
1062
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.
|
|
1063
818
|
|
|
@@ -1077,18 +832,14 @@ class GoogleSheetApp(APIApplication):
|
|
|
1077
832
|
"""
|
|
1078
833
|
if not spreadsheetId:
|
|
1079
834
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1080
|
-
|
|
1081
835
|
if not ranges or not isinstance(ranges, list) or len(ranges) == 0:
|
|
1082
836
|
raise ValueError("ranges must be a non-empty list")
|
|
1083
|
-
|
|
1084
837
|
url = f"{self.base_url}/{spreadsheetId}/values:batchClear"
|
|
1085
|
-
|
|
1086
838
|
request_body = {"ranges": ranges}
|
|
1087
|
-
|
|
1088
839
|
response = self._post(url, data=request_body)
|
|
1089
840
|
return self._handle_response(response)
|
|
1090
841
|
|
|
1091
|
-
def batch_get_values_by_data_filter(
|
|
842
|
+
async def batch_get_values_by_data_filter(
|
|
1092
843
|
self,
|
|
1093
844
|
spreadsheetId: str,
|
|
1094
845
|
data_filters: list[dict],
|
|
@@ -1120,57 +871,26 @@ class GoogleSheetApp(APIApplication):
|
|
|
1120
871
|
"""
|
|
1121
872
|
if not spreadsheetId:
|
|
1122
873
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1123
|
-
|
|
1124
|
-
if (
|
|
1125
|
-
not data_filters
|
|
1126
|
-
or not isinstance(data_filters, list)
|
|
1127
|
-
or len(data_filters) == 0
|
|
1128
|
-
):
|
|
874
|
+
if not data_filters or not isinstance(data_filters, list) or len(data_filters) == 0:
|
|
1129
875
|
raise ValueError("data_filters must be a non-empty list")
|
|
1130
|
-
|
|
1131
876
|
if major_dimension and major_dimension not in ["ROWS", "COLUMNS"]:
|
|
1132
877
|
raise ValueError('major_dimension must be either "ROWS" or "COLUMNS"')
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
"
|
|
1137
|
-
"FORMULA",
|
|
1138
|
-
]:
|
|
1139
|
-
raise ValueError(
|
|
1140
|
-
'value_render_option must be either "FORMATTED_VALUE", "UNFORMATTED_VALUE", or "FORMULA"'
|
|
1141
|
-
)
|
|
1142
|
-
|
|
1143
|
-
if date_time_render_option and date_time_render_option not in [
|
|
1144
|
-
"SERIAL_NUMBER",
|
|
1145
|
-
"FORMATTED_STRING",
|
|
1146
|
-
]:
|
|
1147
|
-
raise ValueError(
|
|
1148
|
-
'date_time_render_option must be either "SERIAL_NUMBER" or "FORMATTED_STRING"'
|
|
1149
|
-
)
|
|
1150
|
-
|
|
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"')
|
|
1151
882
|
url = f"{self.base_url}/{spreadsheetId}/values:batchGetByDataFilter"
|
|
1152
|
-
|
|
1153
883
|
request_body: dict[str, Any] = {"dataFilters": data_filters}
|
|
1154
|
-
|
|
1155
|
-
# Add optional parameters if provided
|
|
1156
884
|
if major_dimension:
|
|
1157
885
|
request_body["majorDimension"] = major_dimension
|
|
1158
|
-
|
|
1159
886
|
if value_render_option:
|
|
1160
887
|
request_body["valueRenderOption"] = value_render_option
|
|
1161
|
-
|
|
1162
888
|
if date_time_render_option:
|
|
1163
889
|
request_body["dateTimeRenderOption"] = date_time_render_option
|
|
1164
|
-
|
|
1165
890
|
response = self._post(url, data=request_body)
|
|
1166
891
|
return self._handle_response(response)
|
|
1167
892
|
|
|
1168
|
-
def copy_sheet_to_spreadsheet(
|
|
1169
|
-
self,
|
|
1170
|
-
spreadsheetId: str,
|
|
1171
|
-
sheet_id: int,
|
|
1172
|
-
destination_spreadsheetId: str,
|
|
1173
|
-
) -> dict[str, Any]:
|
|
893
|
+
async def copy_sheet_to_spreadsheet(self, spreadsheetId: str, sheet_id: int, destination_spreadsheetId: str) -> dict[str, Any]:
|
|
1174
894
|
"""
|
|
1175
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.
|
|
1176
896
|
|
|
@@ -1192,21 +912,16 @@ class GoogleSheetApp(APIApplication):
|
|
|
1192
912
|
"""
|
|
1193
913
|
if not spreadsheetId:
|
|
1194
914
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1195
|
-
|
|
1196
915
|
if sheet_id is None:
|
|
1197
916
|
raise ValueError("sheet_id cannot be empty")
|
|
1198
|
-
|
|
1199
917
|
if not destination_spreadsheetId:
|
|
1200
918
|
raise ValueError("destination_spreadsheetId cannot be empty")
|
|
1201
|
-
|
|
1202
919
|
url = f"{self.base_url}/{spreadsheetId}/sheets/{sheet_id}:copyTo"
|
|
1203
|
-
|
|
1204
920
|
request_body = {"destinationSpreadsheetId": destination_spreadsheetId}
|
|
1205
|
-
|
|
1206
921
|
response = self._post(url, data=request_body)
|
|
1207
922
|
return self._handle_response(response)
|
|
1208
923
|
|
|
1209
|
-
def write_values_to_sheet(
|
|
924
|
+
async def write_values_to_sheet(
|
|
1210
925
|
self,
|
|
1211
926
|
spreadsheetId: str,
|
|
1212
927
|
sheet_name: str,
|
|
@@ -1238,39 +953,23 @@ class GoogleSheetApp(APIApplication):
|
|
|
1238
953
|
"""
|
|
1239
954
|
if not spreadsheetId:
|
|
1240
955
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1241
|
-
|
|
1242
956
|
if not sheet_name:
|
|
1243
957
|
raise ValueError("sheet_name cannot be empty")
|
|
1244
|
-
|
|
1245
958
|
if not values or not isinstance(values, list) or len(values) == 0:
|
|
1246
959
|
raise ValueError("values must be a non-empty 2D list")
|
|
1247
|
-
|
|
1248
960
|
if value_input_option not in ["RAW", "USER_ENTERED"]:
|
|
1249
|
-
raise ValueError(
|
|
1250
|
-
'value_input_option must be either "RAW" or "USER_ENTERED"'
|
|
1251
|
-
)
|
|
1252
|
-
|
|
1253
|
-
# Determine the range based on first_cell_location
|
|
961
|
+
raise ValueError('value_input_option must be either "RAW" or "USER_ENTERED"')
|
|
1254
962
|
if first_cell_location:
|
|
1255
|
-
# Update specific range starting from first_cell_location
|
|
1256
963
|
range_str = f"{sheet_name}!{first_cell_location}"
|
|
1257
964
|
else:
|
|
1258
|
-
# Append to the sheet (no specific range)
|
|
1259
965
|
range_str = f"{sheet_name}"
|
|
1260
|
-
|
|
1261
966
|
url = f"{self.base_url}/{spreadsheetId}/values/{range_str}"
|
|
1262
|
-
|
|
1263
|
-
params = {
|
|
1264
|
-
"valueInputOption": value_input_option,
|
|
1265
|
-
"includeValuesInResponse": include_values_in_response,
|
|
1266
|
-
}
|
|
1267
|
-
|
|
967
|
+
params = {"valueInputOption": value_input_option, "includeValuesInResponse": include_values_in_response}
|
|
1268
968
|
data = {"values": values}
|
|
1269
|
-
|
|
1270
969
|
response = self._put(url, data=data, params=params)
|
|
1271
970
|
return self._handle_response(response)
|
|
1272
971
|
|
|
1273
|
-
def append_values(
|
|
972
|
+
async def append_values(
|
|
1274
973
|
self,
|
|
1275
974
|
spreadsheetId: str,
|
|
1276
975
|
range: str,
|
|
@@ -1306,74 +1005,35 @@ class GoogleSheetApp(APIApplication):
|
|
|
1306
1005
|
"""
|
|
1307
1006
|
if not spreadsheetId:
|
|
1308
1007
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1309
|
-
|
|
1310
1008
|
if not range:
|
|
1311
1009
|
raise ValueError("range cannot be empty")
|
|
1312
|
-
|
|
1313
1010
|
if not value_input_option:
|
|
1314
1011
|
raise ValueError("value_input_option cannot be empty")
|
|
1315
|
-
|
|
1316
1012
|
if value_input_option not in ["RAW", "USER_ENTERED"]:
|
|
1317
|
-
raise ValueError(
|
|
1318
|
-
'value_input_option must be either "RAW" or "USER_ENTERED"'
|
|
1319
|
-
)
|
|
1320
|
-
|
|
1013
|
+
raise ValueError('value_input_option must be either "RAW" or "USER_ENTERED"')
|
|
1321
1014
|
if not values or not isinstance(values, list) or len(values) == 0:
|
|
1322
1015
|
raise ValueError("values must be a non-empty 2D list")
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
"
|
|
1327
|
-
]:
|
|
1328
|
-
raise ValueError(
|
|
1329
|
-
'insert_data_option must be either "OVERWRITE" or "INSERT_ROWS"'
|
|
1330
|
-
)
|
|
1331
|
-
|
|
1332
|
-
if response_value_render_option and response_value_render_option not in [
|
|
1333
|
-
"FORMATTED_VALUE",
|
|
1334
|
-
"UNFORMATTED_VALUE",
|
|
1335
|
-
"FORMULA",
|
|
1336
|
-
]:
|
|
1337
|
-
raise ValueError(
|
|
1338
|
-
'response_value_render_option must be either "FORMATTED_VALUE", "UNFORMATTED_VALUE", or "FORMULA"'
|
|
1339
|
-
)
|
|
1340
|
-
|
|
1341
|
-
if (
|
|
1342
|
-
response_date_time_render_option
|
|
1343
|
-
and response_date_time_render_option
|
|
1344
|
-
not in ["SERIAL_NUMBER", "FORMATTED_STRING"]
|
|
1345
|
-
):
|
|
1346
|
-
raise ValueError(
|
|
1347
|
-
'response_date_time_render_option must be either "SERIAL_NUMBER" or "FORMATTED_STRING"'
|
|
1348
|
-
)
|
|
1349
|
-
|
|
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"')
|
|
1350
1022
|
url = f"{self.base_url}/{spreadsheetId}/values/{range}:append"
|
|
1351
|
-
|
|
1352
1023
|
params: dict[str, Any] = {"valueInputOption": value_input_option}
|
|
1353
|
-
|
|
1354
|
-
# Add optional parameters if provided
|
|
1355
1024
|
if insert_data_option:
|
|
1356
1025
|
params["insertDataOption"] = insert_data_option
|
|
1357
|
-
|
|
1358
1026
|
if include_values_in_response is not None:
|
|
1359
1027
|
params["includeValuesInResponse"] = include_values_in_response
|
|
1360
|
-
|
|
1361
1028
|
if response_value_render_option:
|
|
1362
1029
|
params["responseValueRenderOption"] = response_value_render_option
|
|
1363
|
-
|
|
1364
1030
|
if response_date_time_render_option:
|
|
1365
1031
|
params["responseDateTimeRenderOption"] = response_date_time_render_option
|
|
1366
|
-
|
|
1367
1032
|
data = {"values": values}
|
|
1368
|
-
|
|
1369
1033
|
response = self._post(url, data=data, params=params)
|
|
1370
1034
|
return self._handle_response(response)
|
|
1371
1035
|
|
|
1372
|
-
def clear_basic_filter(
|
|
1373
|
-
self,
|
|
1374
|
-
spreadsheetId: str,
|
|
1375
|
-
sheet_id: int,
|
|
1376
|
-
) -> dict[str, Any]:
|
|
1036
|
+
async def clear_basic_filter(self, spreadsheetId: str, sheet_id: int) -> dict[str, Any]:
|
|
1377
1037
|
"""
|
|
1378
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.
|
|
1379
1039
|
|
|
@@ -1393,22 +1053,14 @@ class GoogleSheetApp(APIApplication):
|
|
|
1393
1053
|
"""
|
|
1394
1054
|
if not spreadsheetId:
|
|
1395
1055
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1396
|
-
|
|
1397
1056
|
if sheet_id < 0:
|
|
1398
1057
|
raise ValueError("sheet_id must be non-negative")
|
|
1399
|
-
|
|
1400
1058
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
1401
|
-
|
|
1402
1059
|
request_body = {"requests": [{"clearBasicFilter": {"sheetId": sheet_id}}]}
|
|
1403
|
-
|
|
1404
1060
|
response = self._post(url, data=request_body)
|
|
1405
1061
|
return self._handle_response(response)
|
|
1406
1062
|
|
|
1407
|
-
def delete_sheet(
|
|
1408
|
-
self,
|
|
1409
|
-
spreadsheetId: str,
|
|
1410
|
-
sheet_id: int,
|
|
1411
|
-
) -> dict[str, Any]:
|
|
1063
|
+
async def delete_sheet(self, spreadsheetId: str, sheet_id: int) -> dict[str, Any]:
|
|
1412
1064
|
"""
|
|
1413
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.
|
|
1414
1066
|
|
|
@@ -1428,23 +1080,15 @@ class GoogleSheetApp(APIApplication):
|
|
|
1428
1080
|
"""
|
|
1429
1081
|
if not spreadsheetId:
|
|
1430
1082
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1431
|
-
|
|
1432
1083
|
if sheet_id < 0:
|
|
1433
1084
|
raise ValueError("sheet_id must be non-negative")
|
|
1434
|
-
|
|
1435
1085
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
1436
|
-
|
|
1437
1086
|
request_body = {"requests": [{"deleteSheet": {"sheetId": sheet_id}}]}
|
|
1438
|
-
|
|
1439
1087
|
response = self._post(url, data=request_body)
|
|
1440
1088
|
return self._handle_response(response)
|
|
1441
1089
|
|
|
1442
|
-
def discover_tables(
|
|
1443
|
-
self,
|
|
1444
|
-
spreadsheetId: str,
|
|
1445
|
-
min_rows: int = 2,
|
|
1446
|
-
min_columns: int = 1,
|
|
1447
|
-
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
|
|
1448
1092
|
) -> dict[str, Any]:
|
|
1449
1093
|
"""
|
|
1450
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.
|
|
@@ -1467,55 +1111,31 @@ class GoogleSheetApp(APIApplication):
|
|
|
1467
1111
|
"""
|
|
1468
1112
|
if not spreadsheetId:
|
|
1469
1113
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1470
|
-
|
|
1471
1114
|
if min_rows < 1:
|
|
1472
1115
|
raise ValueError("min_rows must be at least 1")
|
|
1473
|
-
|
|
1474
1116
|
if min_columns < 1:
|
|
1475
1117
|
raise ValueError("min_columns must be at least 1")
|
|
1476
|
-
|
|
1477
1118
|
if not 0 <= min_confidence <= 1:
|
|
1478
1119
|
raise ValueError("min_confidence must be between 0.0 and 1.0")
|
|
1479
|
-
|
|
1480
|
-
# Get spreadsheet structure
|
|
1481
|
-
spreadsheet = self.get_spreadsheet_metadata(spreadsheetId)
|
|
1482
|
-
|
|
1120
|
+
spreadsheet = await self.get_spreadsheet_metadata(spreadsheetId)
|
|
1483
1121
|
tables = []
|
|
1484
|
-
|
|
1485
1122
|
for sheet in spreadsheet.get("sheets", []):
|
|
1486
1123
|
sheet_properties = sheet.get("properties", {})
|
|
1487
1124
|
sheet_id = sheet_properties.get("sheetId")
|
|
1488
1125
|
sheet_title = sheet_properties.get("title", "Sheet1")
|
|
1489
|
-
|
|
1490
|
-
# Analyze sheet for tables using helper function
|
|
1491
1126
|
sheet_tables = analyze_sheet_for_tables(
|
|
1492
|
-
self.get_values,
|
|
1493
|
-
spreadsheetId,
|
|
1494
|
-
sheet_id,
|
|
1495
|
-
sheet_title,
|
|
1496
|
-
min_rows,
|
|
1497
|
-
min_columns,
|
|
1498
|
-
min_confidence,
|
|
1127
|
+
self.get_values, spreadsheetId, sheet_id, sheet_title, min_rows, min_columns, min_confidence
|
|
1499
1128
|
)
|
|
1500
|
-
|
|
1501
1129
|
tables.extend(sheet_tables)
|
|
1502
|
-
|
|
1503
1130
|
return {
|
|
1504
1131
|
"spreadsheetId": spreadsheetId,
|
|
1505
1132
|
"total_tables": len(tables),
|
|
1506
1133
|
"tables": tables,
|
|
1507
|
-
"analysis_parameters": {
|
|
1508
|
-
"min_rows": min_rows,
|
|
1509
|
-
"min_columns": min_columns,
|
|
1510
|
-
},
|
|
1134
|
+
"analysis_parameters": {"min_rows": min_rows, "min_columns": min_columns},
|
|
1511
1135
|
}
|
|
1512
1136
|
|
|
1513
|
-
def analyze_table_schema(
|
|
1514
|
-
self,
|
|
1515
|
-
spreadsheetId: str,
|
|
1516
|
-
table_name: str,
|
|
1517
|
-
sheet_name: str | None = None,
|
|
1518
|
-
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
|
|
1519
1139
|
) -> dict[str, Any]:
|
|
1520
1140
|
"""
|
|
1521
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.
|
|
@@ -1538,28 +1158,17 @@ class GoogleSheetApp(APIApplication):
|
|
|
1538
1158
|
"""
|
|
1539
1159
|
if not spreadsheetId:
|
|
1540
1160
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1541
|
-
|
|
1542
1161
|
if not table_name:
|
|
1543
1162
|
raise ValueError("table_name cannot be empty")
|
|
1544
|
-
|
|
1545
1163
|
if not 1 <= sample_size <= 1000:
|
|
1546
1164
|
raise ValueError("sample_size must be between 1 and 1000")
|
|
1547
|
-
|
|
1548
|
-
# Get spreadsheet structure
|
|
1549
|
-
spreadsheet = self.get_spreadsheet_metadata(spreadsheetId)
|
|
1550
|
-
|
|
1551
|
-
# Find the target table
|
|
1165
|
+
spreadsheet = await self.get_spreadsheet_metadata(spreadsheetId)
|
|
1552
1166
|
target_table = None
|
|
1553
|
-
|
|
1554
1167
|
for sheet in spreadsheet.get("sheets", []):
|
|
1555
1168
|
sheet_properties = sheet.get("properties", {})
|
|
1556
1169
|
sheet_title = sheet_properties.get("title", "Sheet1")
|
|
1557
|
-
|
|
1558
|
-
# If sheet_name is specified, only look in that sheet
|
|
1559
1170
|
if sheet_name and sheet_title != sheet_name:
|
|
1560
1171
|
continue
|
|
1561
|
-
|
|
1562
|
-
# Get tables in this sheet
|
|
1563
1172
|
sheet_tables = analyze_sheet_for_tables(
|
|
1564
1173
|
self.get_values,
|
|
1565
1174
|
spreadsheetId,
|
|
@@ -1569,35 +1178,20 @@ class GoogleSheetApp(APIApplication):
|
|
|
1569
1178
|
min_columns=1,
|
|
1570
1179
|
min_confidence=0.3,
|
|
1571
1180
|
)
|
|
1572
|
-
|
|
1573
1181
|
for table in sheet_tables:
|
|
1574
1182
|
if table_name == "auto":
|
|
1575
|
-
|
|
1576
|
-
if target_table is None or (
|
|
1577
|
-
table["rows"] * table["columns"]
|
|
1578
|
-
> target_table["rows"] * target_table["columns"]
|
|
1579
|
-
):
|
|
1183
|
+
if target_table is None or table["rows"] * table["columns"] > target_table["rows"] * target_table["columns"]:
|
|
1580
1184
|
target_table = table
|
|
1581
1185
|
elif table["table_name"] == table_name:
|
|
1582
1186
|
target_table = table
|
|
1583
1187
|
break
|
|
1584
|
-
|
|
1585
1188
|
if target_table and table_name != "auto":
|
|
1586
1189
|
break
|
|
1587
|
-
|
|
1588
1190
|
if not target_table:
|
|
1589
1191
|
raise ValueError(f"Table '{table_name}' not found in spreadsheet")
|
|
1192
|
+
return analyze_table_schema(self.get_values, spreadsheetId, target_table, sample_size)
|
|
1590
1193
|
|
|
1591
|
-
|
|
1592
|
-
return analyze_table_schema(
|
|
1593
|
-
self.get_values, spreadsheetId, target_table, sample_size
|
|
1594
|
-
)
|
|
1595
|
-
|
|
1596
|
-
def set_basic_filter(
|
|
1597
|
-
self,
|
|
1598
|
-
spreadsheetId: str,
|
|
1599
|
-
filter: dict,
|
|
1600
|
-
) -> dict[str, Any]:
|
|
1194
|
+
async def set_basic_filter(self, spreadsheetId: str, filter: dict) -> dict[str, Any]:
|
|
1601
1195
|
"""
|
|
1602
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.
|
|
1603
1197
|
|
|
@@ -1626,27 +1220,19 @@ class GoogleSheetApp(APIApplication):
|
|
|
1626
1220
|
"""
|
|
1627
1221
|
if not spreadsheetId:
|
|
1628
1222
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1629
|
-
|
|
1630
1223
|
if not filter:
|
|
1631
1224
|
raise ValueError("filter cannot be empty")
|
|
1632
|
-
|
|
1633
|
-
# Validate required filter fields
|
|
1634
1225
|
if "range" not in filter:
|
|
1635
1226
|
raise ValueError("filter must contain 'range' field")
|
|
1636
|
-
|
|
1637
|
-
# Validate required filter fields using Google API naming convention
|
|
1638
1227
|
range_data = filter["range"]
|
|
1639
1228
|
if "sheetId" not in range_data:
|
|
1640
1229
|
raise ValueError("filter range must contain 'sheetId' field")
|
|
1641
|
-
|
|
1642
1230
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
1643
|
-
|
|
1644
1231
|
request_body = {"requests": [{"setBasicFilter": {"filter": filter}}]}
|
|
1645
|
-
|
|
1646
1232
|
response = self._post(url, data=request_body)
|
|
1647
1233
|
return self._handle_response(response)
|
|
1648
1234
|
|
|
1649
|
-
def format_cells(
|
|
1235
|
+
async def format_cells(
|
|
1650
1236
|
self,
|
|
1651
1237
|
spreadsheetId: str,
|
|
1652
1238
|
worksheetId: int,
|
|
@@ -1654,34 +1240,26 @@ class GoogleSheetApp(APIApplication):
|
|
|
1654
1240
|
startColumnIndex: int,
|
|
1655
1241
|
endRowIndex: int,
|
|
1656
1242
|
endColumnIndex: int,
|
|
1657
|
-
# Text formatting
|
|
1658
1243
|
bold: bool | None = None,
|
|
1659
1244
|
italic: bool | None = None,
|
|
1660
1245
|
underline: bool | None = None,
|
|
1661
1246
|
strikethrough: bool | None = None,
|
|
1662
1247
|
fontSize: int | None = None,
|
|
1663
1248
|
fontFamily: str | None = None,
|
|
1664
|
-
# Colors
|
|
1665
1249
|
backgroundRed: float | None = None,
|
|
1666
1250
|
backgroundGreen: float | None = None,
|
|
1667
1251
|
backgroundBlue: float | None = None,
|
|
1668
1252
|
textRed: float | None = None,
|
|
1669
1253
|
textGreen: float | None = None,
|
|
1670
1254
|
textBlue: float | None = None,
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
# Text wrapping
|
|
1675
|
-
wrapStrategy: str
|
|
1676
|
-
| None = None, # "OVERFLOW_CELL", "LEGACY_WRAP", "CLIP", "WRAP"
|
|
1677
|
-
# Number format
|
|
1255
|
+
horizontalAlignment: str | None = None,
|
|
1256
|
+
verticalAlignment: str | None = None,
|
|
1257
|
+
wrapStrategy: str | None = None,
|
|
1678
1258
|
numberFormat: str | None = None,
|
|
1679
|
-
# Borders
|
|
1680
1259
|
borderTop: dict | None = None,
|
|
1681
1260
|
borderBottom: dict | None = None,
|
|
1682
1261
|
borderLeft: dict | None = None,
|
|
1683
1262
|
borderRight: dict | None = None,
|
|
1684
|
-
# Merge cells
|
|
1685
1263
|
mergeCells: bool = False,
|
|
1686
1264
|
) -> dict[str, Any]:
|
|
1687
1265
|
"""
|
|
@@ -1737,25 +1315,14 @@ class GoogleSheetApp(APIApplication):
|
|
|
1737
1315
|
"""
|
|
1738
1316
|
if not spreadsheetId:
|
|
1739
1317
|
raise ValueError("spreadsheetId cannot be empty")
|
|
1740
|
-
|
|
1741
1318
|
if worksheetId < 0:
|
|
1742
1319
|
raise ValueError("worksheetId must be non-negative")
|
|
1743
|
-
|
|
1744
|
-
if (
|
|
1745
|
-
startRowIndex < 0
|
|
1746
|
-
or startColumnIndex < 0
|
|
1747
|
-
or endRowIndex < 0
|
|
1748
|
-
or endColumnIndex < 0
|
|
1749
|
-
):
|
|
1320
|
+
if startRowIndex < 0 or startColumnIndex < 0 or endRowIndex < 0 or (endColumnIndex < 0):
|
|
1750
1321
|
raise ValueError("All indices must be non-negative")
|
|
1751
|
-
|
|
1752
1322
|
if startRowIndex >= endRowIndex:
|
|
1753
1323
|
raise ValueError("endRowIndex must be greater than startRowIndex")
|
|
1754
|
-
|
|
1755
1324
|
if startColumnIndex >= endColumnIndex:
|
|
1756
1325
|
raise ValueError("endColumnIndex must be greater than startColumnIndex")
|
|
1757
|
-
|
|
1758
|
-
# Validate color values if provided
|
|
1759
1326
|
for color_name, color_value in [
|
|
1760
1327
|
("backgroundRed", backgroundRed),
|
|
1761
1328
|
("backgroundGreen", backgroundGreen),
|
|
@@ -1764,37 +1331,18 @@ class GoogleSheetApp(APIApplication):
|
|
|
1764
1331
|
("textGreen", textGreen),
|
|
1765
1332
|
("textBlue", textBlue),
|
|
1766
1333
|
]:
|
|
1767
|
-
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):
|
|
1768
1335
|
raise ValueError(f"{color_name} must be between 0.0 and 1.0")
|
|
1769
|
-
|
|
1770
1336
|
if fontSize is not None and fontSize <= 0:
|
|
1771
1337
|
raise ValueError("fontSize must be positive")
|
|
1772
|
-
|
|
1773
|
-
if horizontalAlignment and horizontalAlignment not in [
|
|
1774
|
-
"LEFT",
|
|
1775
|
-
"CENTER",
|
|
1776
|
-
"RIGHT",
|
|
1777
|
-
]:
|
|
1338
|
+
if horizontalAlignment and horizontalAlignment not in ["LEFT", "CENTER", "RIGHT"]:
|
|
1778
1339
|
raise ValueError('horizontalAlignment must be "LEFT", "CENTER", or "RIGHT"')
|
|
1779
|
-
|
|
1780
1340
|
if verticalAlignment and verticalAlignment not in ["TOP", "MIDDLE", "BOTTOM"]:
|
|
1781
1341
|
raise ValueError('verticalAlignment must be "TOP", "MIDDLE", or "BOTTOM"')
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
"OVERFLOW_CELL",
|
|
1785
|
-
"LEGACY_WRAP",
|
|
1786
|
-
"CLIP",
|
|
1787
|
-
"WRAP",
|
|
1788
|
-
]:
|
|
1789
|
-
raise ValueError(
|
|
1790
|
-
'wrapStrategy must be "OVERFLOW_CELL", "LEGACY_WRAP", "CLIP", or "WRAP"'
|
|
1791
|
-
)
|
|
1792
|
-
|
|
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"')
|
|
1793
1344
|
url = f"{self.base_url}/{spreadsheetId}:batchUpdate"
|
|
1794
|
-
|
|
1795
1345
|
requests = []
|
|
1796
|
-
|
|
1797
|
-
# Handle cell merging first if requested
|
|
1798
1346
|
if mergeCells:
|
|
1799
1347
|
requests.append(
|
|
1800
1348
|
{
|
|
@@ -1810,11 +1358,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
1810
1358
|
}
|
|
1811
1359
|
}
|
|
1812
1360
|
)
|
|
1813
|
-
|
|
1814
|
-
# Build the cell format request
|
|
1815
1361
|
cell_format = {}
|
|
1816
|
-
|
|
1817
|
-
# Text format
|
|
1818
1362
|
text_format = {}
|
|
1819
1363
|
if bold is not None:
|
|
1820
1364
|
text_format["bold"] = bold
|
|
@@ -1828,9 +1372,7 @@ class GoogleSheetApp(APIApplication):
|
|
|
1828
1372
|
text_format["fontSize"] = fontSize
|
|
1829
1373
|
if fontFamily is not None:
|
|
1830
1374
|
text_format["fontFamily"] = fontFamily
|
|
1831
|
-
|
|
1832
|
-
# Text color
|
|
1833
|
-
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])):
|
|
1834
1376
|
text_color = {}
|
|
1835
1377
|
if textRed is not None:
|
|
1836
1378
|
text_color["red"] = textRed
|
|
@@ -1840,15 +1382,9 @@ class GoogleSheetApp(APIApplication):
|
|
|
1840
1382
|
text_color["blue"] = textBlue
|
|
1841
1383
|
if text_color:
|
|
1842
1384
|
text_format["foregroundColor"] = {"rgbColor": text_color}
|
|
1843
|
-
|
|
1844
1385
|
if text_format:
|
|
1845
1386
|
cell_format["textFormat"] = text_format
|
|
1846
|
-
|
|
1847
|
-
# Background color
|
|
1848
|
-
if any(
|
|
1849
|
-
color is not None
|
|
1850
|
-
for color in [backgroundRed, backgroundGreen, backgroundBlue]
|
|
1851
|
-
):
|
|
1387
|
+
if any((color is not None for color in [backgroundRed, backgroundGreen, backgroundBlue])):
|
|
1852
1388
|
background_color = {}
|
|
1853
1389
|
if backgroundRed is not None:
|
|
1854
1390
|
background_color["red"] = backgroundRed
|
|
@@ -1858,35 +1394,19 @@ class GoogleSheetApp(APIApplication):
|
|
|
1858
1394
|
background_color["blue"] = backgroundBlue
|
|
1859
1395
|
if background_color:
|
|
1860
1396
|
cell_format["backgroundColorStyle"] = {"rgbColor": background_color}
|
|
1861
|
-
|
|
1862
|
-
# Alignment
|
|
1863
1397
|
if horizontalAlignment or verticalAlignment:
|
|
1864
1398
|
cell_format["horizontalAlignment"] = horizontalAlignment
|
|
1865
1399
|
cell_format["verticalAlignment"] = verticalAlignment
|
|
1866
|
-
|
|
1867
|
-
# Text wrapping
|
|
1868
1400
|
if wrapStrategy:
|
|
1869
1401
|
cell_format["wrapStrategy"] = wrapStrategy
|
|
1870
|
-
|
|
1871
|
-
# Number format
|
|
1872
1402
|
if numberFormat:
|
|
1873
1403
|
cell_format["numberFormat"] = {"type": "TEXT", "pattern": numberFormat}
|
|
1874
|
-
|
|
1875
|
-
# Borders
|
|
1876
1404
|
borders = {}
|
|
1877
|
-
for border_side, border_config in [
|
|
1878
|
-
("top", borderTop),
|
|
1879
|
-
("bottom", borderBottom),
|
|
1880
|
-
("left", borderLeft),
|
|
1881
|
-
("right", borderRight),
|
|
1882
|
-
]:
|
|
1405
|
+
for border_side, border_config in [("top", borderTop), ("bottom", borderBottom), ("left", borderLeft), ("right", borderRight)]:
|
|
1883
1406
|
if border_config:
|
|
1884
1407
|
borders[border_side] = border_config
|
|
1885
|
-
|
|
1886
1408
|
if borders:
|
|
1887
1409
|
cell_format["borders"] = borders
|
|
1888
|
-
|
|
1889
|
-
# Add cell formatting request if any formatting is specified
|
|
1890
1410
|
if cell_format:
|
|
1891
1411
|
requests.append(
|
|
1892
1412
|
{
|
|
@@ -1903,7 +1423,6 @@ class GoogleSheetApp(APIApplication):
|
|
|
1903
1423
|
}
|
|
1904
1424
|
}
|
|
1905
1425
|
)
|
|
1906
|
-
|
|
1907
1426
|
request_body = {"requests": requests}
|
|
1908
1427
|
response = self._post(url, data=request_body)
|
|
1909
1428
|
return self._handle_response(response)
|