google-workspace-mcp 1.0.5__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google_workspace_mcp/models.py +486 -0
- google_workspace_mcp/services/calendar.py +14 -4
- google_workspace_mcp/services/drive.py +268 -18
- google_workspace_mcp/services/sheets_service.py +273 -35
- google_workspace_mcp/services/slides.py +242 -53
- google_workspace_mcp/tools/calendar.py +99 -88
- google_workspace_mcp/tools/docs_tools.py +67 -33
- google_workspace_mcp/tools/drive.py +288 -25
- google_workspace_mcp/tools/gmail.py +95 -39
- google_workspace_mcp/tools/sheets_tools.py +112 -46
- google_workspace_mcp/tools/slides.py +317 -46
- {google_workspace_mcp-1.0.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/METADATA +4 -3
- {google_workspace_mcp-1.0.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/RECORD +15 -14
- {google_workspace_mcp-1.0.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/WHEEL +0 -0
- {google_workspace_mcp-1.0.5.dist-info → google_workspace_mcp-1.2.0.dist-info}/entry_points.txt +0 -0
@@ -37,7 +37,11 @@ class SlidesService(BaseGoogleService):
|
|
37
37
|
Presentation data dictionary or error information
|
38
38
|
"""
|
39
39
|
try:
|
40
|
-
return
|
40
|
+
return (
|
41
|
+
self.service.presentations()
|
42
|
+
.get(presentationId=presentation_id)
|
43
|
+
.execute()
|
44
|
+
)
|
41
45
|
except Exception as e:
|
42
46
|
return self.handle_api_error("get_presentation", e)
|
43
47
|
|
@@ -57,7 +61,9 @@ class SlidesService(BaseGoogleService):
|
|
57
61
|
except Exception as e:
|
58
62
|
return self.handle_api_error("create_presentation", e)
|
59
63
|
|
60
|
-
def create_slide(
|
64
|
+
def create_slide(
|
65
|
+
self, presentation_id: str, layout: str = "TITLE_AND_BODY"
|
66
|
+
) -> dict[str, Any]:
|
61
67
|
"""
|
62
68
|
Add a new slide to an existing presentation.
|
63
69
|
|
@@ -80,11 +86,17 @@ class SlidesService(BaseGoogleService):
|
|
80
86
|
}
|
81
87
|
]
|
82
88
|
|
83
|
-
logger.info(
|
89
|
+
logger.info(
|
90
|
+
f"Sending API request to create slide: {json.dumps(requests[0], indent=2)}"
|
91
|
+
)
|
84
92
|
|
85
93
|
# Execute the request
|
86
94
|
response = (
|
87
|
-
self.service.presentations()
|
95
|
+
self.service.presentations()
|
96
|
+
.batchUpdate(
|
97
|
+
presentationId=presentation_id, body={"requests": requests}
|
98
|
+
)
|
99
|
+
.execute()
|
88
100
|
)
|
89
101
|
|
90
102
|
logger.info(f"API response: {json.dumps(response, indent=2)}")
|
@@ -161,12 +173,20 @@ class SlidesService(BaseGoogleService):
|
|
161
173
|
},
|
162
174
|
]
|
163
175
|
|
164
|
-
logger.info(
|
165
|
-
|
176
|
+
logger.info(
|
177
|
+
f"Sending API request to create shape: {json.dumps(requests[0], indent=2)}"
|
178
|
+
)
|
179
|
+
logger.info(
|
180
|
+
f"Sending API request to insert text: {json.dumps(requests[1], indent=2)}"
|
181
|
+
)
|
166
182
|
|
167
183
|
# Execute the request
|
168
184
|
response = (
|
169
|
-
self.service.presentations()
|
185
|
+
self.service.presentations()
|
186
|
+
.batchUpdate(
|
187
|
+
presentationId=presentation_id, body={"requests": requests}
|
188
|
+
)
|
189
|
+
.execute()
|
170
190
|
)
|
171
191
|
|
172
192
|
logger.info(f"API response: {json.dumps(response, indent=2)}")
|
@@ -205,7 +225,9 @@ class SlidesService(BaseGoogleService):
|
|
205
225
|
Response data or error information
|
206
226
|
"""
|
207
227
|
try:
|
208
|
-
logger.info(
|
228
|
+
logger.info(
|
229
|
+
f"Adding formatted text to slide {slide_id}, position={position}, size={size}"
|
230
|
+
)
|
209
231
|
logger.info(f"Text content: '{formatted_text[:100]}...'")
|
210
232
|
logger.info(
|
211
233
|
f"Checking for formatting: bold={'**' in formatted_text}, italic={'*' in formatted_text}, code={'`' in formatted_text}"
|
@@ -240,17 +262,23 @@ class SlidesService(BaseGoogleService):
|
|
240
262
|
]
|
241
263
|
|
242
264
|
# Log the shape creation request
|
243
|
-
logger.info(
|
265
|
+
logger.info(
|
266
|
+
f"Sending API request to create shape: {json.dumps(create_requests[0], indent=2)}"
|
267
|
+
)
|
244
268
|
|
245
269
|
# Execute creation request
|
246
270
|
creation_response = (
|
247
271
|
self.service.presentations()
|
248
|
-
.batchUpdate(
|
272
|
+
.batchUpdate(
|
273
|
+
presentationId=presentation_id, body={"requests": create_requests}
|
274
|
+
)
|
249
275
|
.execute()
|
250
276
|
)
|
251
277
|
|
252
278
|
# Log the response
|
253
|
-
logger.info(
|
279
|
+
logger.info(
|
280
|
+
f"API response for shape creation: {json.dumps(creation_response, indent=2)}"
|
281
|
+
)
|
254
282
|
|
255
283
|
# Process the formatted text
|
256
284
|
# First, remove formatting markers to get plain text
|
@@ -274,7 +302,9 @@ class SlidesService(BaseGoogleService):
|
|
274
302
|
]
|
275
303
|
|
276
304
|
# Log the text insertion request
|
277
|
-
logger.info(
|
305
|
+
logger.info(
|
306
|
+
f"Sending API request to insert plain text: {json.dumps(text_request[0], indent=2)}"
|
307
|
+
)
|
278
308
|
|
279
309
|
# Execute text insertion
|
280
310
|
text_response = (
|
@@ -287,7 +317,9 @@ class SlidesService(BaseGoogleService):
|
|
287
317
|
)
|
288
318
|
|
289
319
|
# Log the response
|
290
|
-
logger.info(
|
320
|
+
logger.info(
|
321
|
+
f"API response for plain text insertion: {json.dumps(text_response, indent=2)}"
|
322
|
+
)
|
291
323
|
|
292
324
|
# Now generate style requests if there's formatting to apply
|
293
325
|
if "**" in formatted_text or "*" in formatted_text:
|
@@ -364,9 +396,13 @@ class SlidesService(BaseGoogleService):
|
|
364
396
|
if style_requests:
|
365
397
|
try:
|
366
398
|
# Log the style requests
|
367
|
-
logger.info(
|
399
|
+
logger.info(
|
400
|
+
f"Sending API request to apply text styling with {len(style_requests)} style requests"
|
401
|
+
)
|
368
402
|
for i, req in enumerate(style_requests):
|
369
|
-
logger.info(
|
403
|
+
logger.info(
|
404
|
+
f"Style request {i + 1}: {json.dumps(req, indent=2)}"
|
405
|
+
)
|
370
406
|
|
371
407
|
# Execute style requests
|
372
408
|
style_response = (
|
@@ -379,9 +415,13 @@ class SlidesService(BaseGoogleService):
|
|
379
415
|
)
|
380
416
|
|
381
417
|
# Log the response
|
382
|
-
logger.info(
|
418
|
+
logger.info(
|
419
|
+
f"API response for text styling: {json.dumps(style_response, indent=2)}"
|
420
|
+
)
|
383
421
|
except Exception as style_error:
|
384
|
-
logger.warning(
|
422
|
+
logger.warning(
|
423
|
+
f"Failed to apply text styles: {str(style_error)}"
|
424
|
+
)
|
385
425
|
logger.exception("Style application error details")
|
386
426
|
|
387
427
|
return {
|
@@ -440,7 +480,9 @@ class SlidesService(BaseGoogleService):
|
|
440
480
|
},
|
441
481
|
}
|
442
482
|
}
|
443
|
-
logger.info(
|
483
|
+
logger.info(
|
484
|
+
f"Sending API request to create shape for bullet list: {json.dumps(log_data, indent=2)}"
|
485
|
+
)
|
444
486
|
|
445
487
|
# Create requests
|
446
488
|
requests = [
|
@@ -476,15 +518,23 @@ class SlidesService(BaseGoogleService):
|
|
476
518
|
]
|
477
519
|
|
478
520
|
# Log the text insertion
|
479
|
-
logger.info(
|
521
|
+
logger.info(
|
522
|
+
f"Sending API request to insert bullet text: {json.dumps(requests[1], indent=2)}"
|
523
|
+
)
|
480
524
|
|
481
525
|
# Execute the request to create shape and insert text
|
482
526
|
response = (
|
483
|
-
self.service.presentations()
|
527
|
+
self.service.presentations()
|
528
|
+
.batchUpdate(
|
529
|
+
presentationId=presentation_id, body={"requests": requests}
|
530
|
+
)
|
531
|
+
.execute()
|
484
532
|
)
|
485
533
|
|
486
534
|
# Log the response
|
487
|
-
logger.info(
|
535
|
+
logger.info(
|
536
|
+
f"API response for bullet list creation: {json.dumps(response, indent=2)}"
|
537
|
+
)
|
488
538
|
|
489
539
|
# Now add bullet formatting
|
490
540
|
try:
|
@@ -493,14 +543,18 @@ class SlidesService(BaseGoogleService):
|
|
493
543
|
{
|
494
544
|
"createParagraphBullets": {
|
495
545
|
"objectId": element_id,
|
496
|
-
"textRange": {
|
546
|
+
"textRange": {
|
547
|
+
"type": "ALL"
|
548
|
+
}, # Apply to all text in the shape
|
497
549
|
"bulletPreset": "BULLET_DISC_CIRCLE_SQUARE",
|
498
550
|
}
|
499
551
|
}
|
500
552
|
]
|
501
553
|
|
502
554
|
# Log the bullet formatting request
|
503
|
-
logger.info(
|
555
|
+
logger.info(
|
556
|
+
f"Sending API request to apply bullet formatting: {json.dumps(bullet_request[0], indent=2)}"
|
557
|
+
)
|
504
558
|
|
505
559
|
bullet_response = (
|
506
560
|
self.service.presentations()
|
@@ -512,9 +566,13 @@ class SlidesService(BaseGoogleService):
|
|
512
566
|
)
|
513
567
|
|
514
568
|
# Log the response
|
515
|
-
logger.info(
|
569
|
+
logger.info(
|
570
|
+
f"API response for bullet formatting: {json.dumps(bullet_response, indent=2)}"
|
571
|
+
)
|
516
572
|
except Exception as bullet_error:
|
517
|
-
logger.warning(
|
573
|
+
logger.warning(
|
574
|
+
f"Failed to apply bullet formatting: {str(bullet_error)}"
|
575
|
+
)
|
518
576
|
# No fallback here - the text is already added, just without bullets
|
519
577
|
|
520
578
|
return {
|
@@ -527,7 +585,9 @@ class SlidesService(BaseGoogleService):
|
|
527
585
|
except Exception as e:
|
528
586
|
return self.handle_api_error("add_bulleted_list", e)
|
529
587
|
|
530
|
-
def create_presentation_from_markdown(
|
588
|
+
def create_presentation_from_markdown(
|
589
|
+
self, title: str, markdown_content: str
|
590
|
+
) -> dict[str, Any]:
|
531
591
|
"""
|
532
592
|
Create a Google Slides presentation from Markdown content using markdowndeck.
|
533
593
|
|
@@ -545,9 +605,13 @@ class SlidesService(BaseGoogleService):
|
|
545
605
|
credentials = gauth.get_credentials()
|
546
606
|
|
547
607
|
# Use markdowndeck to create the presentation
|
548
|
-
result = create_presentation(
|
608
|
+
result = create_presentation(
|
609
|
+
markdown=markdown_content, title=title, credentials=credentials
|
610
|
+
)
|
549
611
|
|
550
|
-
logger.info(
|
612
|
+
logger.info(
|
613
|
+
f"Successfully created presentation with ID: {result.get('presentationId')}"
|
614
|
+
)
|
551
615
|
|
552
616
|
# The presentation data is already in the expected format from markdowndeck
|
553
617
|
return result
|
@@ -568,7 +632,11 @@ class SlidesService(BaseGoogleService):
|
|
568
632
|
"""
|
569
633
|
try:
|
570
634
|
# Get the presentation with slide details
|
571
|
-
presentation =
|
635
|
+
presentation = (
|
636
|
+
self.service.presentations()
|
637
|
+
.get(presentationId=presentation_id)
|
638
|
+
.execute()
|
639
|
+
)
|
572
640
|
|
573
641
|
# Extract slide information
|
574
642
|
slides = []
|
@@ -587,9 +655,13 @@ class SlidesService(BaseGoogleService):
|
|
587
655
|
if "textElements" in element["shape"]["text"]:
|
588
656
|
# Extract text content
|
589
657
|
text_parts = []
|
590
|
-
for text_element in element["shape"]["text"][
|
658
|
+
for text_element in element["shape"]["text"][
|
659
|
+
"textElements"
|
660
|
+
]:
|
591
661
|
if "textRun" in text_element:
|
592
|
-
text_parts.append(
|
662
|
+
text_parts.append(
|
663
|
+
text_element["textRun"].get("content", "")
|
664
|
+
)
|
593
665
|
element_content = "".join(text_parts)
|
594
666
|
elif "image" in element:
|
595
667
|
element_type = "image"
|
@@ -597,9 +669,7 @@ class SlidesService(BaseGoogleService):
|
|
597
669
|
element_content = element["image"]["contentUrl"]
|
598
670
|
elif "table" in element:
|
599
671
|
element_type = "table"
|
600
|
-
element_content = (
|
601
|
-
f"Table with {element['table'].get('rows', 0)} rows, {element['table'].get('columns', 0)} columns"
|
602
|
-
)
|
672
|
+
element_content = f"Table with {element['table'].get('rows', 0)} rows, {element['table'].get('columns', 0)} columns"
|
603
673
|
|
604
674
|
# Add to elements if we found content
|
605
675
|
if element_type and element_content:
|
@@ -613,7 +683,10 @@ class SlidesService(BaseGoogleService):
|
|
613
683
|
|
614
684
|
# Get speaker notes if present
|
615
685
|
notes = ""
|
616
|
-
if
|
686
|
+
if (
|
687
|
+
"slideProperties" in slide
|
688
|
+
and "notesPage" in slide["slideProperties"]
|
689
|
+
):
|
617
690
|
notes_page = slide["slideProperties"]["notesPage"]
|
618
691
|
if "pageElements" in notes_page:
|
619
692
|
for element in notes_page["pageElements"]:
|
@@ -623,9 +696,13 @@ class SlidesService(BaseGoogleService):
|
|
623
696
|
and "textElements" in element["shape"]["text"]
|
624
697
|
):
|
625
698
|
note_parts = []
|
626
|
-
for text_element in element["shape"]["text"][
|
699
|
+
for text_element in element["shape"]["text"][
|
700
|
+
"textElements"
|
701
|
+
]:
|
627
702
|
if "textRun" in text_element:
|
628
|
-
note_parts.append(
|
703
|
+
note_parts.append(
|
704
|
+
text_element["textRun"].get("content", "")
|
705
|
+
)
|
629
706
|
if note_parts:
|
630
707
|
notes = "".join(note_parts)
|
631
708
|
|
@@ -657,14 +734,22 @@ class SlidesService(BaseGoogleService):
|
|
657
734
|
# Define the delete request
|
658
735
|
requests = [{"deleteObject": {"objectId": slide_id}}]
|
659
736
|
|
660
|
-
logger.info(
|
737
|
+
logger.info(
|
738
|
+
f"Sending API request to delete slide: {json.dumps(requests[0], indent=2)}"
|
739
|
+
)
|
661
740
|
|
662
741
|
# Execute the request
|
663
742
|
response = (
|
664
|
-
self.service.presentations()
|
743
|
+
self.service.presentations()
|
744
|
+
.batchUpdate(
|
745
|
+
presentationId=presentation_id, body={"requests": requests}
|
746
|
+
)
|
747
|
+
.execute()
|
665
748
|
)
|
666
749
|
|
667
|
-
logger.info(
|
750
|
+
logger.info(
|
751
|
+
f"API response for slide deletion: {json.dumps(response, indent=2)}"
|
752
|
+
)
|
668
753
|
|
669
754
|
return {
|
670
755
|
"presentationId": presentation_id,
|
@@ -724,7 +809,9 @@ class SlidesService(BaseGoogleService):
|
|
724
809
|
"height": {"magnitude": size[1], "unit": "PT"},
|
725
810
|
}
|
726
811
|
|
727
|
-
logger.info(
|
812
|
+
logger.info(
|
813
|
+
f"Sending API request to create image: {json.dumps(create_image_request, indent=2)}"
|
814
|
+
)
|
728
815
|
|
729
816
|
# Execute the request
|
730
817
|
response = (
|
@@ -739,7 +826,9 @@ class SlidesService(BaseGoogleService):
|
|
739
826
|
# Extract the image ID from the response
|
740
827
|
if "replies" in response and len(response["replies"]) > 0:
|
741
828
|
image_id = response["replies"][0].get("createImage", {}).get("objectId")
|
742
|
-
logger.info(
|
829
|
+
logger.info(
|
830
|
+
f"API response for image creation: {json.dumps(response, indent=2)}"
|
831
|
+
)
|
743
832
|
return {
|
744
833
|
"presentationId": presentation_id,
|
745
834
|
"slideId": slide_id,
|
@@ -804,7 +893,9 @@ class SlidesService(BaseGoogleService):
|
|
804
893
|
}
|
805
894
|
}
|
806
895
|
|
807
|
-
logger.info(
|
896
|
+
logger.info(
|
897
|
+
f"Sending API request to create table: {json.dumps(create_table_request, indent=2)}"
|
898
|
+
)
|
808
899
|
|
809
900
|
# Execute table creation
|
810
901
|
response = (
|
@@ -816,7 +907,9 @@ class SlidesService(BaseGoogleService):
|
|
816
907
|
.execute()
|
817
908
|
)
|
818
909
|
|
819
|
-
logger.info(
|
910
|
+
logger.info(
|
911
|
+
f"API response for table creation: {json.dumps(response, indent=2)}"
|
912
|
+
)
|
820
913
|
|
821
914
|
# Populate the table if data is provided
|
822
915
|
if data:
|
@@ -841,7 +934,9 @@ class SlidesService(BaseGoogleService):
|
|
841
934
|
)
|
842
935
|
|
843
936
|
if text_requests:
|
844
|
-
logger.info(
|
937
|
+
logger.info(
|
938
|
+
f"Sending API request to populate table with {len(text_requests)} cell entries"
|
939
|
+
)
|
845
940
|
table_text_response = (
|
846
941
|
self.service.presentations()
|
847
942
|
.batchUpdate(
|
@@ -850,7 +945,9 @@ class SlidesService(BaseGoogleService):
|
|
850
945
|
)
|
851
946
|
.execute()
|
852
947
|
)
|
853
|
-
logger.info(
|
948
|
+
logger.info(
|
949
|
+
f"API response for table population: {json.dumps(table_text_response, indent=2)}"
|
950
|
+
)
|
854
951
|
|
855
952
|
return {
|
856
953
|
"presentationId": presentation_id,
|
@@ -891,14 +988,22 @@ class SlidesService(BaseGoogleService):
|
|
891
988
|
}
|
892
989
|
]
|
893
990
|
|
894
|
-
logger.info(
|
991
|
+
logger.info(
|
992
|
+
f"Sending API request to add slide notes: {json.dumps(requests[0], indent=2)}"
|
993
|
+
)
|
895
994
|
|
896
995
|
# Execute the request
|
897
996
|
response = (
|
898
|
-
self.service.presentations()
|
997
|
+
self.service.presentations()
|
998
|
+
.batchUpdate(
|
999
|
+
presentationId=presentation_id, body={"requests": requests}
|
1000
|
+
)
|
1001
|
+
.execute()
|
899
1002
|
)
|
900
1003
|
|
901
|
-
logger.info(
|
1004
|
+
logger.info(
|
1005
|
+
f"API response for slide notes: {json.dumps(response, indent=2)}"
|
1006
|
+
)
|
902
1007
|
|
903
1008
|
return {
|
904
1009
|
"presentationId": presentation_id,
|
@@ -909,7 +1014,9 @@ class SlidesService(BaseGoogleService):
|
|
909
1014
|
except Exception as e:
|
910
1015
|
return self.handle_api_error("add_slide_notes", e)
|
911
1016
|
|
912
|
-
def duplicate_slide(
|
1017
|
+
def duplicate_slide(
|
1018
|
+
self, presentation_id: str, slide_id: str, insert_at_index: int | None = None
|
1019
|
+
) -> dict[str, Any]:
|
913
1020
|
"""
|
914
1021
|
Duplicate a slide in a presentation.
|
915
1022
|
|
@@ -929,7 +1036,9 @@ class SlidesService(BaseGoogleService):
|
|
929
1036
|
if insert_at_index is not None:
|
930
1037
|
duplicate_request["duplicateObject"]["insertionIndex"] = insert_at_index
|
931
1038
|
|
932
|
-
logger.info(
|
1039
|
+
logger.info(
|
1040
|
+
f"Sending API request to duplicate slide: {json.dumps(duplicate_request, indent=2)}"
|
1041
|
+
)
|
933
1042
|
|
934
1043
|
# Execute the duplicate request
|
935
1044
|
response = (
|
@@ -941,12 +1050,16 @@ class SlidesService(BaseGoogleService):
|
|
941
1050
|
.execute()
|
942
1051
|
)
|
943
1052
|
|
944
|
-
logger.info(
|
1053
|
+
logger.info(
|
1054
|
+
f"API response for slide duplication: {json.dumps(response, indent=2)}"
|
1055
|
+
)
|
945
1056
|
|
946
1057
|
# Extract the duplicated slide ID
|
947
1058
|
new_slide_id = None
|
948
1059
|
if "replies" in response and len(response["replies"]) > 0:
|
949
|
-
new_slide_id =
|
1060
|
+
new_slide_id = (
|
1061
|
+
response["replies"][0].get("duplicateObject", {}).get("objectId")
|
1062
|
+
)
|
950
1063
|
|
951
1064
|
return {
|
952
1065
|
"presentationId": presentation_id,
|
@@ -957,3 +1070,79 @@ class SlidesService(BaseGoogleService):
|
|
957
1070
|
}
|
958
1071
|
except Exception as e:
|
959
1072
|
return self.handle_api_error("duplicate_slide", e)
|
1073
|
+
|
1074
|
+
def embed_sheets_chart(
|
1075
|
+
self,
|
1076
|
+
presentation_id: str,
|
1077
|
+
slide_id: str,
|
1078
|
+
spreadsheet_id: str,
|
1079
|
+
chart_id: int,
|
1080
|
+
position: tuple[float, float],
|
1081
|
+
size: tuple[float, float],
|
1082
|
+
) -> dict[str, Any]:
|
1083
|
+
"""
|
1084
|
+
Embeds a chart from Google Sheets into a Google Slides presentation.
|
1085
|
+
|
1086
|
+
Args:
|
1087
|
+
presentation_id: The ID of the presentation.
|
1088
|
+
slide_id: The ID of the slide to add the chart to.
|
1089
|
+
spreadsheet_id: The ID of the Google Sheet containing the chart.
|
1090
|
+
chart_id: The ID of the chart within the sheet.
|
1091
|
+
position: Tuple of (x, y) coordinates for position in PT.
|
1092
|
+
size: Tuple of (width, height) for the chart size in PT.
|
1093
|
+
|
1094
|
+
Returns:
|
1095
|
+
The API response from the batchUpdate call.
|
1096
|
+
"""
|
1097
|
+
try:
|
1098
|
+
element_id = f"chart_{chart_id}_{int(__import__('time').time() * 1000)}"
|
1099
|
+
logger.info(
|
1100
|
+
f"Embedding chart {chart_id} from sheet {spreadsheet_id} into slide {slide_id}"
|
1101
|
+
)
|
1102
|
+
|
1103
|
+
requests = [
|
1104
|
+
{
|
1105
|
+
"createSheetsChart": {
|
1106
|
+
"objectId": element_id,
|
1107
|
+
"spreadsheetId": spreadsheet_id,
|
1108
|
+
"chartId": chart_id,
|
1109
|
+
"linkingMode": "LINKED",
|
1110
|
+
"elementProperties": {
|
1111
|
+
"pageObjectId": slide_id,
|
1112
|
+
"size": {
|
1113
|
+
"width": {"magnitude": size[0], "unit": "PT"},
|
1114
|
+
"height": {"magnitude": size[1], "unit": "PT"},
|
1115
|
+
},
|
1116
|
+
"transform": {
|
1117
|
+
"scaleX": 1,
|
1118
|
+
"scaleY": 1,
|
1119
|
+
"translateX": position[0],
|
1120
|
+
"translateY": position[1],
|
1121
|
+
"unit": "PT",
|
1122
|
+
},
|
1123
|
+
},
|
1124
|
+
}
|
1125
|
+
}
|
1126
|
+
]
|
1127
|
+
|
1128
|
+
response = self.batch_update(presentation_id, requests)
|
1129
|
+
if response.get("error"):
|
1130
|
+
raise ValueError(
|
1131
|
+
response.get("message", "Batch update for chart embedding failed")
|
1132
|
+
)
|
1133
|
+
|
1134
|
+
created_element_id = (
|
1135
|
+
response.get("replies", [{}])[0]
|
1136
|
+
.get("createSheetsChart", {})
|
1137
|
+
.get("objectId")
|
1138
|
+
)
|
1139
|
+
|
1140
|
+
return {
|
1141
|
+
"success": True,
|
1142
|
+
"presentation_id": presentation_id,
|
1143
|
+
"slide_id": slide_id,
|
1144
|
+
"element_id": created_element_id or element_id,
|
1145
|
+
}
|
1146
|
+
|
1147
|
+
except Exception as e:
|
1148
|
+
return self.handle_api_error("embed_sheets_chart", e)
|