Files
soleprint/artery/veins/google/core/sheets.py
2025-12-31 08:34:18 -03:00

131 lines
3.9 KiB
Python

"""
Google Sheets API client.
Isolated client that can run without FastAPI.
"""
from typing import Optional
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
class GoogleSheetsError(Exception):
"""Sheets API error."""
pass
class GoogleSheetsClient:
"""
Google Sheets API client.
Provides methods to read spreadsheet data.
"""
def __init__(self, credentials: Credentials):
"""
Initialize Sheets client.
Args:
credentials: Google OAuth2 credentials
"""
self.credentials = credentials
self.service = build("sheets", "v4", credentials=credentials)
def get_spreadsheet_metadata(self, spreadsheet_id: str) -> dict:
"""
Get spreadsheet metadata (title, sheets, etc.).
Args:
spreadsheet_id: The spreadsheet ID
Returns:
Spreadsheet metadata
"""
try:
result = self.service.spreadsheets().get(
spreadsheetId=spreadsheet_id
).execute()
return result
except HttpError as e:
raise GoogleSheetsError(f"Failed to get spreadsheet: {e}")
def get_sheet_values(
self,
spreadsheet_id: str,
range_name: str,
value_render_option: str = "FORMATTED_VALUE",
) -> list[list]:
"""
Get values from a sheet range.
Args:
spreadsheet_id: The spreadsheet ID
range_name: A1 notation range (e.g., 'Sheet1!A1:D10')
value_render_option: How values should be rendered
- FORMATTED_VALUE: Values formatted as strings (default)
- UNFORMATTED_VALUE: Values in calculated format
- FORMULA: Formulas
Returns:
List of rows, each row is a list of cell values
"""
try:
result = self.service.spreadsheets().values().get(
spreadsheetId=spreadsheet_id,
range=range_name,
valueRenderOption=value_render_option,
).execute()
return result.get("values", [])
except HttpError as e:
raise GoogleSheetsError(f"Failed to get values: {e}")
def get_all_sheets(self, spreadsheet_id: str) -> list[dict]:
"""
Get list of all sheets in a spreadsheet.
Args:
spreadsheet_id: The spreadsheet ID
Returns:
List of sheet metadata (title, id, index, etc.)
"""
metadata = self.get_spreadsheet_metadata(spreadsheet_id)
return [
{
"title": sheet["properties"]["title"],
"sheet_id": sheet["properties"]["sheetId"],
"index": sheet["properties"]["index"],
"row_count": sheet["properties"]["gridProperties"].get("rowCount", 0),
"column_count": sheet["properties"]["gridProperties"].get("columnCount", 0),
}
for sheet in metadata.get("sheets", [])
]
def batch_get_values(
self,
spreadsheet_id: str,
ranges: list[str],
value_render_option: str = "FORMATTED_VALUE",
) -> dict:
"""
Get multiple ranges in a single request.
Args:
spreadsheet_id: The spreadsheet ID
ranges: List of A1 notation ranges
value_render_option: How values should be rendered
Returns:
Dict with spreadsheetId and valueRanges list
"""
try:
result = self.service.spreadsheets().values().batchGet(
spreadsheetId=spreadsheet_id,
ranges=ranges,
valueRenderOption=value_render_option,
).execute()
return result
except HttpError as e:
raise GoogleSheetsError(f"Failed to batch get values: {e}")