Google Workspace Write APIs
The Google Workspace integration provides write access to Google Sheets, Docs, Slides,
and Drive via the Google API v4/v1. All operations are authenticated through
GoogleWorkspaceClient, which supports OAuth2, service accounts, and multi-account
registry lookup.
Installation
pip install siege-utilities[analytics]
This installs google-api-python-client, google-auth-oauthlib, and
google-auth-httplib2.
Authentication
All write operations require an authenticated GoogleWorkspaceClient.
1Password Integration (Recommended)
The from_1password() factory method downloads a credential JSON document from
1Password and auto-detects whether it is a service account key or an OAuth
client secret:
from siege_utilities.analytics.google_workspace import GoogleWorkspaceClient
# Service account (server-to-server, no browser required)
client = GoogleWorkspaceClient.from_1password(
item_title="Google Service Account - siege_utilities",
vault="Employee",
account="Siege_Analytics",
)
# OAuth (opens browser on first use, caches token afterward)
client = GoogleWorkspaceClient.from_1password(
item_title="Google OAuth Client - siege_utilities",
vault="Personal",
account="Siege_Analytics",
)
One-time GCP setup for a service account:
# Create the service account
gcloud iam service-accounts create siege-utilities-workspace \
--project=windy-art-489721-j3 \
--display-name="siege_utilities Workspace"
# Enable Workspace APIs
gcloud services enable sheets.googleapis.com docs.googleapis.com \
slides.googleapis.com drive.googleapis.com \
--project=windy-art-489721-j3
# Download the JSON key
gcloud iam service-accounts keys create ~/siege-utilities-sa.json \
--iam-account=siege-utilities-workspace@windy-art-489721-j3.iam.gserviceaccount.com \
--project=windy-art-489721-j3
# Store in 1Password and delete the local file
op document create ~/siege-utilities-sa.json \
--title "Google Service Account - siege_utilities" \
--vault Employee --account Siege_Analytics
rm ~/siege-utilities-sa.json
One-time OAuth setup (for personal Drive access):
python scripts/google_workspace_auth.py
This opens a browser for Google sign-in and caches the token at
~/.siege/tokens/workspace_token.json.
Note
Service accounts have their own Drive space. To access files in a user’s
personal Drive, share those files with the service account email
(siege-utilities-workspace@windy-art-489721-j3.iam.gserviceaccount.com).
Direct Authentication
You can also authenticate without 1Password:
Service account from a file:
client = GoogleWorkspaceClient.from_service_account(
service_account_file="/path/to/service-account.json",
)
OAuth2 with explicit credentials:
client = GoogleWorkspaceClient.from_oauth(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
token_file="workspace_token.json",
)
From a GoogleAccount registry (see Multi-Account Management):
from siege_utilities.config import GoogleAccountRegistry
registry = GoogleAccountRegistry(config_path=Path("google_accounts.json"))
client = GoogleWorkspaceClient.from_registry(registry)
Google Sheets
Create spreadsheets, write and read data, manage tabs, and round-trip DataFrames.
from siege_utilities.analytics.google_sheets import (
create_spreadsheet, write_values, read_values, append_rows,
write_dataframe, read_dataframe, add_sheet,
get_spreadsheet_metadata, copy_spreadsheet,
)
# Create a spreadsheet with named tabs
spreadsheet_id = create_spreadsheet(client, "Q1 Report", sheet_names=["Data", "Summary"])
# Write raw values
write_values(client, spreadsheet_id, "Data!A1", [
["Name", "Revenue", "Region"],
["Acme", 150000, "Northeast"],
["Beta", 230000, "Southeast"],
])
# Append rows after existing data
append_rows(client, spreadsheet_id, "Data", [["Gamma", 90000, "West"]])
# Write a pandas DataFrame
write_dataframe(client, spreadsheet_id, df, sheet_name="Summary")
# Read it back
df_round_trip = read_dataframe(client, spreadsheet_id, "Summary")
# Add a tab
add_sheet(client, spreadsheet_id, "Notes")
# Metadata and copy
meta = get_spreadsheet_metadata(client, spreadsheet_id)
copy_id = copy_spreadsheet(client, spreadsheet_id, "Q1 Report (Copy)")
Google Slides
Create presentations, add slides, and populate them with text boxes and images.
from siege_utilities.analytics.google_slides import (
create_presentation, get_presentation, copy_presentation,
add_blank_slide, create_textbox, insert_text, insert_image,
)
pres_id = create_presentation(client, "Quarterly Review")
# Get the default first slide
pres = get_presentation(client, pres_id)
title_slide = pres["slides"][0]["objectId"]
# Add content to it
create_textbox(client, pres_id, title_slide, "Q1 2026 Review",
left=50, top=80, width=600, height=60)
# Add a new blank slide with content
slide_id = add_blank_slide(client, pres_id)
create_textbox(client, pres_id, slide_id, "Revenue grew 23% YoY",
left=50, top=100, width=600, height=40)
# Copy the presentation
copy_id = copy_presentation(client, pres_id, "Quarterly Review (Draft)")
Google Docs
Create documents, insert structured content (paragraphs, tables, images), and perform find/replace operations.
from siege_utilities.analytics.google_docs import (
create_document, get_document, copy_document,
read_document_text, insert_paragraph, insert_text,
insert_table, insert_image, replace_text,
)
doc_id = create_document(client, "Meeting Notes")
# Insert a heading
insert_paragraph(client, doc_id, "Action Items", index=1, heading="HEADING_1")
# Insert body text
doc = get_document(client, doc_id)
end = doc["body"]["content"][-1]["endIndex"] - 1
insert_paragraph(client, doc_id, "Review budget allocations by Friday.", index=end)
# Insert a table
doc = get_document(client, doc_id)
end = doc["body"]["content"][-1]["endIndex"] - 1
insert_table(client, doc_id, rows=3, cols=2, index=end)
# Template substitution
replace_text(client, doc_id, "{{DATE}}", "2026-03-09")
# Read full text back
text = read_document_text(client, doc_id)
# Copy
copy_id = copy_document(client, doc_id, "Meeting Notes (Archive)")
Drive Utilities
The GoogleWorkspaceClient includes Drive operations for files it creates:
# Copy any file (spreadsheet, doc, presentation)
new_id = client.copy_file(file_id, "New Title")
# Share with a user
client.share_file(file_id, "colleague@example.com", role="writer")
# Move to a folder
client.move_to_folder(file_id, folder_id)
Multi-Account Management
The GoogleAccount model and GoogleAccountRegistry support managing
multiple Google accounts (OAuth and service account) per user.
from siege_utilities.config.models.google_account import (
GoogleAccount, GoogleAccountType, GoogleAccountStatus,
)
from siege_utilities.config import GoogleAccountRegistry
from siege_utilities.config.models.person import Person
# Create accounts
oauth_acct = GoogleAccount(
google_account_id="personal",
email="user@example.com",
display_name="Personal",
account_type=GoogleAccountType.OAUTH,
is_default=True,
oauth_integration_name="google-workspace",
token_file="~/.siege/tokens/token.json",
)
svc_acct = GoogleAccount(
google_account_id="pipeline",
email="svc@project.iam.gserviceaccount.com",
display_name="Pipeline SA",
account_type=GoogleAccountType.SERVICE_ACCOUNT,
service_account_ref="op://Infra/google-sa/credential",
)
# Registry: register, persist, load
registry = GoogleAccountRegistry()
registry.register(oauth_acct)
registry.register(svc_acct)
registry.save(Path("google_accounts.json"))
# Build client from registry
client = GoogleWorkspaceClient.from_registry(registry)
# Person integration
person = Person(person_id="dheeraj", name="Dheeraj Chand")
person.add_google_account(oauth_acct)
default = person.get_default_google_account()
# Migrate from legacy OAuthIntegration
from siege_utilities.config.google_account_registry import migrate_single_account
migrated = migrate_single_account(legacy_oauth, email="user@example.com")
Notebook
See notebooks/18_Google_Workspace.ipynb for a full walkthrough using
elect.info onboarding content written to live Google Drive files.
API Reference
Google Workspace base client for Docs, Sheets, and Slides write APIs.
Provides shared authentication and service-building logic reused by the Sheets, Slides, and Docs service modules.
Authentication follows the same patterns as GoogleAnalyticsConnector: - OAuth2 (interactive flow with token file) - Service Account (from 1Password or file) - Explicit Credentials object
- Usage:
from siege_utilities.analytics.google_workspace import GoogleWorkspaceClient
# OAuth2 with token file client = GoogleWorkspaceClient.from_oauth(
client_id=”…”, client_secret=”…”, token_file=”token.json”,
)
# Service Account from 1Password client = GoogleWorkspaceClient.from_service_account()
# Get a specific API service sheets = client.sheets_service() docs = client.docs_service() slides = client.slides_service()
- class siege_utilities.analytics.google_workspace.GoogleWorkspaceClient[source]
Bases:
objectAuthenticated client that builds Google API service objects.
Do not instantiate directly — use the
from_oauth()orfrom_service_account()class methods.- classmethod from_oauth(client_id, client_secret, token_file=None, redirect_uri='urn:ietf:wg:oauth:2.0:oob', scopes=None)[source]
Authenticate via OAuth2 interactive flow.
If token_file exists and contains a valid/refreshable token, no browser interaction is needed.
- classmethod from_1password(item_title='Google OAuth Client - siege_utilities', vault=None, account='TLTQ3ANAABGCNEK7KIAOTDNK2Q', token_file=None, scopes=None)[source]
Authenticate using credentials stored in a 1Password Document item.
Auto-detects whether the JSON document is an OAuth client secret (has
"installed"or"web"key) or a service account key (has"type": "service_account"), and routes to the appropriate auth flow.For OAuth: runs the installed-app flow (browser required on first use; cached token reused afterward).
For service account: authenticates server-to-server with no browser interaction.
- Parameters:
item_title (str) – Title of the 1Password Document item.
vault (str | None) – 1Password vault name.
account (str | None) – 1Password account shorthand or UUID.
token_file (str | Path | None) – Path to cache the OAuth token (ignored for service accounts). Default:
~/.siege/tokens/workspace_token.json.scopes (List[str] | None) – OAuth scopes (defaults to
WORKSPACE_SCOPES).
- Returns:
Authenticated GoogleWorkspaceClient.
- Return type:
- classmethod from_service_account(service_account_data=None, service_account_file=None, scopes=None)[source]
Authenticate via service account credentials.
Provide service_account_data (dict) or service_account_file (path). If neither is given, attempts to fetch from 1Password via
CredentialManager.get_google_service_account_from_1password().
- classmethod from_credentials(credentials)[source]
Wrap an already-authenticated
google.auth.credentials.Credentials.- Return type:
- classmethod from_account(account, person=None, scopes=None)[source]
Build a client from a
GoogleAccount.For OAuth accounts the method looks for a cached token_file first, then resolves oauth_integration_name from person to get client_id/client_secret for the OAuth flow.
For service accounts the method resolves service_account_ref via
CredentialManager(1Password) or treats it as a file path.- Parameters:
account (GoogleAccount)
person (Optional[Person])
scopes (Optional[List[str]])
- Return type:
- classmethod from_registry(registry, google_account_id=None, person=None, scopes=None)[source]
Build a client from a registry’s default or specified account.
If google_account_id is given, looks it up directly; otherwise uses the registry’s default account.
- Parameters:
registry (GoogleAccountRegistry)
google_account_id (Optional[str])
person (Optional[Person])
scopes (Optional[List[str]])
- Return type:
- property credentials
- batch_update_presentation(presentation_id, requests)[source]
Execute a batch of Slides API requests.
- static spreadsheet_url(spreadsheet_id)[source]
Return the live Google Sheets URL for a spreadsheet ID.
- static presentation_url(presentation_id)[source]
Return the live Google Slides URL for a presentation ID.
- static file_url(file_id, mime_type=None)[source]
Return the live Google Drive URL for a file ID.
If mime_type is provided, returns the appropriate editor URL. Otherwise returns the generic Drive file URL.
- copy_file(file_id, title=None)[source]
Copy a Drive file (spreadsheet, doc, presentation) and return the new ID.
Share a Drive file with a user.
- upload_file(local_path, name=None, mime_type=None, folder_id=None)[source]
Upload a local file to Drive and return its file ID.
- Parameters:
- Returns:
The new Drive file’s ID.
- Return type:
Google Sheets write service.
Provides functions for creating spreadsheets, writing data, managing tabs (sheets), and performing batch updates via the Sheets API v4.
All functions accept a GoogleWorkspaceClient for authentication.
- Usage:
from siege_utilities.analytics.google_workspace import GoogleWorkspaceClient from siege_utilities.analytics.google_sheets import (
create_spreadsheet, write_dataframe, append_rows,
)
client = GoogleWorkspaceClient.from_service_account() spreadsheet_id = create_spreadsheet(client, “Q1 Report”) write_dataframe(client, spreadsheet_id, df)
- siege_utilities.analytics.google_sheets.add_sheet(client, spreadsheet_id, title)[source]
Add a new tab/sheet to an existing spreadsheet.
- siege_utilities.analytics.google_sheets.append_rows(client, spreadsheet_id, range_, values, value_input_option='USER_ENTERED')[source]
Append rows after existing data in a range.
- Parameters:
- Returns:
The API response dict.
- Return type:
- siege_utilities.analytics.google_sheets.batch_update(client, spreadsheet_id, requests)[source]
Execute a batch of Sheets API requests.
Delegates to
GoogleWorkspaceClient.batch_update_spreadsheet().
- siege_utilities.analytics.google_sheets.copy_spreadsheet(client, spreadsheet_id, title=None)[source]
Copy an entire spreadsheet via the Drive API.
- siege_utilities.analytics.google_sheets.create_spreadsheet(client, title, sheet_names=None, folder_id=None)[source]
Create a new Google Spreadsheet and return its ID.
- Parameters:
- Returns:
The spreadsheet ID string.
- Return type:
- siege_utilities.analytics.google_sheets.get_spreadsheet_metadata(client, spreadsheet_id)[source]
Fetch spreadsheet metadata (title, sheets, locale, etc.).
- siege_utilities.analytics.google_sheets.read_dataframe(client, spreadsheet_id, range_='Sheet1', has_header=True)[source]
Read a range from a spreadsheet into a pandas DataFrame.
- siege_utilities.analytics.google_sheets.read_values(client, spreadsheet_id, range_)[source]
Read values from a range.
- siege_utilities.analytics.google_sheets.write_dataframe(client, spreadsheet_id, df, sheet_name='Sheet1', include_header=True, start_cell='A1')[source]
Write a pandas DataFrame to a sheet.
- Parameters:
- Returns:
The API response dict.
- Return type:
- siege_utilities.analytics.google_sheets.write_values(client, spreadsheet_id, range_, values, value_input_option='USER_ENTERED')[source]
Write a 2-D list of values to a range.
- Parameters:
- Returns:
The API response dict.
- Return type:
Google Docs write service.
Provides functions for creating documents, inserting text, tables, images, and performing batch updates via the Docs API v1.
- Usage:
from siege_utilities.analytics.google_workspace import GoogleWorkspaceClient from siege_utilities.analytics.google_docs import (
create_document, insert_text, insert_table,
)
client = GoogleWorkspaceClient.from_service_account() doc_id = create_document(client, “Meeting Notes”) insert_text(client, doc_id, “Agendan”, bold=True) insert_table(client, doc_id, rows=3, cols=2)
- siege_utilities.analytics.google_docs.batch_update(client, document_id, requests)[source]
Execute a batch of Docs API requests.
Delegates to
GoogleWorkspaceClient.batch_update_document().
- siege_utilities.analytics.google_docs.copy_document(client, document_id, title=None)[source]
Copy a document via the Drive API.
- siege_utilities.analytics.google_docs.create_document(client, title, folder_id=None)[source]
Create a new Google Doc and return its document ID.
- siege_utilities.analytics.google_docs.get_document(client, document_id)[source]
Fetch full document metadata and content structure.
- siege_utilities.analytics.google_docs.insert_image(client, document_id, image_uri, index=1, width=None, height=None)[source]
Insert an image from a URL.
The URI must be publicly accessible or a Google Drive file.
- Parameters:
- Returns:
The API response dict.
- Return type:
- siege_utilities.analytics.google_docs.insert_paragraph(client, document_id, text, index=1, heading=None)[source]
Insert a paragraph with optional heading style.
- Parameters:
client – Authenticated GoogleWorkspaceClient.
document_id (str) – Target document ID.
text (str) – Paragraph text (newline is appended automatically).
index (int) – Character index (1-based).
heading (str | None) – Named style (
"HEADING_1"through"HEADING_6","TITLE","SUBTITLE", orNonefor normal text).
- Returns:
The API response dict.
- Return type:
- siege_utilities.analytics.google_docs.insert_table(client, document_id, rows, cols, index=1)[source]
Insert an empty table at the given index.
- siege_utilities.analytics.google_docs.insert_text(client, document_id, text, index=1, bold=False, italic=False, font_size=None)[source]
Insert text at a given index in a document.
- Parameters:
client – Authenticated GoogleWorkspaceClient.
document_id (str) – Target document ID.
text (str) – Text to insert.
index (int) – Character index (1-based; 1 = start of document body).
bold (bool) – Apply bold formatting.
italic (bool) – Apply italic formatting.
font_size (int | None) – Font size in points (
None= default).
- Returns:
The API response dict.
- Return type:
- siege_utilities.analytics.google_docs.read_document_text(client, document_id)[source]
Read the full plain-text content of a document.
Extracts text from all structural elements (paragraphs, tables, etc.).
- siege_utilities.analytics.google_docs.replace_text(client, document_id, find, replace_with, match_case=True)[source]
Find and replace text throughout a document.
Google Slides write service.
Provides functions for creating presentations, adding slides, inserting text and images, and performing batch updates via the Slides API v1.
- Usage:
from siege_utilities.analytics.google_workspace import GoogleWorkspaceClient from siege_utilities.analytics.google_slides import (
create_presentation, add_blank_slide, insert_text,
)
client = GoogleWorkspaceClient.from_service_account() pres_id = create_presentation(client, “Q1 Report”) slide_id = add_blank_slide(client, pres_id) insert_text(client, pres_id, slide_id, “Hello World”)
- siege_utilities.analytics.google_slides.add_blank_slide(client, presentation_id, layout='BLANK', insertion_index=None)[source]
Add a blank slide to a presentation.
- Parameters:
- Returns:
The new slide’s object ID.
- Return type:
- siege_utilities.analytics.google_slides.batch_update(client, presentation_id, requests)[source]
Execute a batch of Slides API requests.
Delegates to
GoogleWorkspaceClient.batch_update_presentation().
- siege_utilities.analytics.google_slides.copy_presentation(client, presentation_id, title=None)[source]
Copy an entire presentation via the Drive API.
- siege_utilities.analytics.google_slides.create_argument_slide(client, presentation_id, argument, slide_index=None, folder_id=None, publish_figures=True)[source]
Add one slide for an Argument.
- Layout is derived from argument.layout:
“full_width” → title / narrative / figure stacked vertically “side_by_side” → title top; narrative left, figure right
A figure is inserted through
temporary_public_figure(): it is uploaded tofolder_id, shared anyone-with-the-link while Slides copies it, then deleted from Drive.- Parameters:
client – Authenticated GoogleWorkspaceClient.
presentation_id (str) – Target presentation ID.
argument – Argument dataclass instance.
slide_index (int | None) – Insertion position. None → append at end.
folder_id (str | None) – Drive folder for the temporary figure file. None → the root of the authenticated account’s My Drive.
publish_figures (bool) – If False, the figure is not uploaded, no anyone-with-the-link permission is created, and the slide is built without it (a WARNING is logged).
- Returns:
Slide object ID.
- Return type:
- siege_utilities.analytics.google_slides.create_presentation(client, title, folder_id=None)[source]
Create a new Google Slides presentation and return its ID.
- siege_utilities.analytics.google_slides.create_report_from_arguments(client, title, arguments, folder_id=None, theme_presentation_id=None, publish_figures=True)[source]
Create a complete presentation from a list of Arguments.
- Public exposure of figures:
The Slides API can only insert an image from a URL that is reachable without authentication. With
publish_figures=True(the default), each chart or map is therefore uploaded to Drive (intofolder_idwhen given), shared anyone-with-the-link (reader) for the duration of itscreateImagerequest, and then deleted from Drive, including when the insert fails. While it exists, anyone holding the URL can fetch the image without signing in. If the delete fails, the file ID is logged at ERROR and the file must be removed manually. Passpublish_figures=Falseto never create an anyone permission; figures are then left out of the slides.
- Parameters:
client – Authenticated GoogleWorkspaceClient.
title (str) – Presentation title.
arguments (List) – List of Argument dataclass instances (one slide each).
folder_id (str | None) – Optional Drive folder to place the presentation, and the temporary figure files, in.
theme_presentation_id (str | None) – If given, copies this presentation first (preserves master slides / theme).
publish_figures (bool) – If False, figures are not uploaded or published and are omitted from the slides.
- Returns:
Presentation ID of the newly created report.
- Return type:
- siege_utilities.analytics.google_slides.create_textbox(client, presentation_id, slide_id, text, left=100, top=100, width=400, height=50)[source]
Create a text box on a slide and populate it with text.
Dimensions are in EMU (English Metric Units). 1 inch = 914400 EMU. For convenience, this function accepts points (1 pt = 12700 EMU) but the values are treated as points internally.
- Parameters:
client – Authenticated GoogleWorkspaceClient.
presentation_id (str) – Target presentation ID.
slide_id (str) – Slide object ID.
text (str) – Text to put in the box.
left (float) – Position in points from top-left.
top (float) – Position in points from top-left.
width (float) – Dimensions in points.
height (float) – Dimensions in points.
- Returns:
The textbox object ID.
- Return type:
- siege_utilities.analytics.google_slides.get_presentation(client, presentation_id)[source]
Fetch full presentation metadata.
- siege_utilities.analytics.google_slides.insert_image(client, presentation_id, slide_id, image_url, left=100, top=100, width=400, height=300)[source]
Insert an image onto a slide from a URL.
The URL must be publicly accessible or a Google Drive file URL.
- Parameters:
client – Authenticated GoogleWorkspaceClient.
presentation_id (str) – Target presentation ID.
slide_id (str) – Slide object ID.
image_url (str) – Public URL of the image.
left (float) – Position in points.
top (float) – Position in points.
width (float) – Size in points.
height (float) – Size in points.
- Returns:
The image object ID.
- Return type:
- siege_utilities.analytics.google_slides.insert_text(client, presentation_id, object_id, text, insertion_index=0)[source]
Insert text into an existing shape or text box.
- Parameters:
- Returns:
The API response dict.
- Return type:
- siege_utilities.analytics.google_slides.temporary_public_figure(client, figure, filename, folder_id=None)[source]
Upload a figure, publish it for the duration of the block, then delete it.
Slides
createImagefetches the image once, at request time, from a URL that must be reachable without authentication, and stores its own copy. This context manager uploads the figure tofolder_id, grants an anyone-with-the-link reader permission, yields the public URL, and in afinallydeletes the Drive file (which removes the permission too), whether or not the block raised.If the delete fails, the file ID is logged at ERROR. The delete error is raised only when the block itself succeeded; an error from the block is never masked by a cleanup failure.
- Parameters:
- Yields:
The public URL of the uploaded image.
- Return type:
- siege_utilities.analytics.google_slides.upload_figure_to_drive(client, figure, filename, folder_id=None)[source]
Save a matplotlib Figure to a temp PNG, upload to Google Drive, return URL.
Warning
The uploaded file is shared anyone-with-the-link (reader) via
GoogleWorkspaceClient.public_urland is left in Drive. Nothing revokes that permission or deletes the file. For inserting a figure into a slide, usetemporary_public_figure(), which deletes the Drive file once the insert has run.- Parameters:
- Returns:
Public URL string for the uploaded image (suitable for insert_image).
- Return type: