131 lines
3.9 KiB
Python
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}")
|