from typing import Annotated from arcade.sdk import ToolContext, tool from arcade.sdk.auth import Google from arcade.sdk.errors import RetryableToolError from arcade_google.models import ( SheetDataInput, Spreadsheet, SpreadsheetProperties, ) from arcade_google.utils import ( build_sheets_service, create_sheet, parse_get_spreadsheet_response, parse_write_to_cell_response, validate_write_to_cell_params, ) @tool( requires_auth=Google( scopes=["https://www.googleapis.com/auth/drive.file"], ) ) def create_spreadsheet( context: ToolContext, title: Annotated[str, "The title of the new spreadsheet"] = "Untitled spreadsheet", data: Annotated[ str | None, "The data to write to the spreadsheet. A JSON string " "(property names enclosed in double quotes) representing a dictionary that " "maps row numbers to dictionaries that map column letters to cell values. " "For example, data[23]['C'] would be the value of the cell in row 23, column C. " "Type hint: dict[int, dict[str, Union[int, float, str, bool]]]", ] = None, ) -> Annotated[dict, "The created spreadsheet's id and title"]: """Create a new spreadsheet with the provided title and data in its first sheet Returns the newly created spreadsheet's id and title """ service = build_sheets_service(context.get_auth_token_or_empty()) try: sheet_data = SheetDataInput(data=data) # type: ignore[arg-type] except Exception as e: msg = "Invalid JSON or unexpected data format for parameter `data`" raise RetryableToolError( message=msg, additional_prompt_content=f"{msg}: {e}", retry_after_ms=100, ) spreadsheet = Spreadsheet( properties=SpreadsheetProperties(title=title), sheets=[create_sheet(sheet_data)], ) body = spreadsheet.model_dump() response = ( service.spreadsheets() .create(body=body, fields="spreadsheetId,spreadsheetUrl,properties/title") .execute() ) return { "title": response["properties"]["title"], "spreadsheetId": response["spreadsheetId"], "spreadsheetUrl": response["spreadsheetUrl"], } @tool( requires_auth=Google( scopes=["https://www.googleapis.com/auth/drive.file"], ) ) async def get_spreadsheet( context: ToolContext, spreadsheet_id: Annotated[str, "The id of the spreadsheet to get"], ) -> Annotated[ dict, "The spreadsheet properties and data for all sheets in the spreadsheet", ]: """ Get the user entered values and formatted values for all cells in all sheets in the spreadsheet along with the spreadsheet's properties """ service = build_sheets_service(context.get_auth_token_or_empty()) response = ( service.spreadsheets() .get( spreadsheetId=spreadsheet_id, includeGridData=True, fields="spreadsheetId,spreadsheetUrl,properties/title,sheets/properties,sheets/data/rowData/values/userEnteredValue,sheets/data/rowData/values/formattedValue,sheets/data/rowData/values/effectiveValue", ) .execute() ) return parse_get_spreadsheet_response(response) @tool( requires_auth=Google( scopes=["https://www.googleapis.com/auth/drive.file"], ) ) def write_to_cell( context: ToolContext, spreadsheet_id: Annotated[str, "The id of the spreadsheet to write to"], column: Annotated[str, "The column string to write to. For example, 'A', 'F', or 'AZ'"], row: Annotated[int, "The row number to write to"], value: Annotated[str, "The value to write to the cell"], sheet_name: Annotated[ str, "The name of the sheet to write to. Defaults to 'Sheet1'" ] = "Sheet1", ) -> Annotated[dict, "The status of the operation"]: """ Write a value to a single cell in a spreadsheet. """ service = build_sheets_service(context.get_auth_token_or_empty()) validate_write_to_cell_params(service, spreadsheet_id, sheet_name, column, row) range_ = f"'{sheet_name}'!{column.upper()}{row}" body = { "range": range_, "majorDimension": "ROWS", "values": [[value]], } sheet_properties = ( service.spreadsheets() .values() .update( spreadsheetId=spreadsheet_id, range=range_, valueInputOption="USER_ENTERED", includeValuesInResponse=True, body=body, ) .execute() ) return parse_write_to_cell_response(sheet_properties)