ops_utils.google_sheets_util

Module to interact with Google Sheets API.

  1"""Module to interact with Google Sheets API."""
  2from typing import Optional
  3from google.auth import default
  4from google.auth.transport.requests import Request
  5import gspread
  6
  7
  8class GoogleSheets:
  9    """Class to interact with Google Sheets API."""
 10
 11    _SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
 12
 13    def __init__(self, service_account_info: Optional[dict] = None):
 14        """
 15        Initialize the GoogleSheets instance using the service account or user credentials.
 16
 17        This method sets up the Google Sheets client using either the provided service account
 18        credentials or the application-default credentials. If no service account information
 19        is provided, ensure that the application-default credentials are properly configured.
 20
 21        **Args:**
 22        - service_account_info (Optional[dict]): A dictionary containing the service account credentials.
 23
 24        **Example:**
 25        To use application-default credentials, run the following command:
 26        ```
 27        gcloud auth application-default login \
 28        --scopes=https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/cloud-platform
 29        ```
 30        """
 31        if service_account_info:
 32            self.gc = gspread.service_account_from_dict(service_account_info)
 33        else:
 34            # This assumes gcloud auth application-default login has been run
 35            creds, _ = default(scopes=self._SCOPES)
 36            creds.refresh(Request())
 37            self.gc = gspread.Client(auth=creds)
 38
 39    def _open_worksheet(self, spreadsheet_id: str, worksheet_name: str) -> gspread.Worksheet:
 40        """
 41        Open a spreadsheet by its ID.
 42
 43        **Args:**
 44        - spreadsheet_id (str): The ID of the Google Sheet.
 45        """
 46        spreadsheet = self.gc.open_by_key(spreadsheet_id)
 47        return spreadsheet.worksheet(worksheet_name)
 48
 49    def update_cell(self, spreadsheet_id: str, worksheet_name: str, cell: str, value: str) -> None:
 50        """
 51        Update a specific cell in the sheet.
 52
 53        **Args:**
 54        - spreadsheet_id (str): Spreadsheet ID.
 55        - worksheet_name (str): Sheet/tab name.
 56        - cell (str): A1-style cell notation.
 57        - value (str): Value to insert.
 58        """
 59        worksheet = self._open_worksheet(spreadsheet_id, worksheet_name)
 60        worksheet.update([[value]], range_name=cell)
 61
 62    def get_cell_value(self, spreadsheet_id: str, worksheet_name: str, cell: str) -> str:
 63        """
 64        Get the value of a specific cell.
 65
 66        **Args:**
 67        - spreadsheet_id (str): Spreadsheet ID.
 68        - worksheet_name (str): Sheet/tab name.
 69        - cell (str): A1-style cell reference.
 70
 71        **Returns:**
 72        - str or None: Cell value or None if empty.
 73        """
 74        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
 75        return ws.acell(cell).value
 76
 77    def get_last_row(self, spreadsheet_id: str, worksheet_name: str) -> int:
 78        """
 79        Get the last non-empty row in the specified column, accounting for trailing empty rows.
 80
 81        **Args:**
 82        - spreadsheet_id (str): Spreadsheet ID.
 83        - worksheet_name (str): Sheet/tab name.
 84
 85        **Returns:**
 86        - int: The last non-empty row number.
 87        """
 88        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
 89        col_values = ws.col_values(1)  # Get all values in the first column
 90        for row_index in range(len(col_values), 0, -1):  # Iterate from the last row to the first
 91            if col_values[row_index - 1]:  # Check if the cell is not empty
 92                return row_index
 93        return 0  # Return 0 if all rows are empty
 94
 95    def get_column_values(self, spreadsheet_id: str, worksheet_name: str, column: str) -> list:
 96        """
 97        Get all values in a specific column in order of row.
 98
 99        **Args:**
100        - spreadsheet_id (str): Spreadsheet ID.
101        - worksheet_name (str): Sheet/tab name.
102        - column (str): Column identifier (e.g., "A" or "1").
103
104        **Returns:**
105        - list: List of values in the column.
106        """
107        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
108
109        # Convert column letter to number if it's a letter
110        if column.isalpha():
111            # gspread uses 1-based indexing for columns
112            column_index = 0
113            for char in column.upper():
114                column_index = column_index * 26 + (ord(char) - ord('A') + 1)
115        else:
116            # If column is already a number
117            column_index = int(column)
118
119        return ws.col_values(column_index)
120
121    def get_worksheet_as_dict(self, spreadsheet_id: str, worksheet_name: str) -> list[dict]:
122        """
123        Get all data from a worksheet as a list of dictionaries.
124
125        The first row is used as the header/keys for the dictionaries.
126        Each subsequent row becomes a dictionary with the header values as keys.
127
128        **Args:**
129        - spreadsheet_id (str): Spreadsheet ID.
130        - worksheet_name (str): Sheet/tab name.
131
132        **Returns:**
133        - list[dict]: List of dictionaries, where each dictionary represents a row
134                      with column headers as keys.
135        """
136        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
137        return ws.get_all_records()
138
139    def batch_update_cells(self, spreadsheet_id: str, worksheet_name: str, updates: list[dict[str, str]]) -> None:
140        """
141        Update multiple cells in a single batch request.
142
143        This method is more efficient than calling update_cell multiple times
144        as it sends all updates in a single API request.
145
146        **Args:**
147        - spreadsheet_id (str): Spreadsheet ID.
148        - worksheet_name (str): Sheet/tab name.
149        - updates (list[dict[str, str]]): List of dictionaries, each containing:
150            - 'cell': A1-style cell notation (e.g., 'A1', 'B5')
151            - 'value': Value to insert
152
153        **Example:**
154        ```python
155        updates = [
156            {'cell': 'A1', 'value': 'Name'},
157            {'cell': 'B1', 'value': 'Age'},
158            {'cell': 'A2', 'value': 'John'},
159            {'cell': 'B2', 'value': '30'}
160        ]
161        gs.batch_update_cells(spreadsheet_id, worksheet_name, updates)
162        ```
163        """
164        worksheet = self._open_worksheet(spreadsheet_id, worksheet_name)
165
166        # Build the batch update data
167        batch_data = []
168        for update in updates:
169            batch_data.append({
170                'range': update['cell'],
171                'values': [[update['value']]]
172            })
173
174        # Perform batch update
175        worksheet.batch_update(batch_data)
class GoogleSheets:
  9class GoogleSheets:
 10    """Class to interact with Google Sheets API."""
 11
 12    _SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
 13
 14    def __init__(self, service_account_info: Optional[dict] = None):
 15        """
 16        Initialize the GoogleSheets instance using the service account or user credentials.
 17
 18        This method sets up the Google Sheets client using either the provided service account
 19        credentials or the application-default credentials. If no service account information
 20        is provided, ensure that the application-default credentials are properly configured.
 21
 22        **Args:**
 23        - service_account_info (Optional[dict]): A dictionary containing the service account credentials.
 24
 25        **Example:**
 26        To use application-default credentials, run the following command:
 27        ```
 28        gcloud auth application-default login \
 29        --scopes=https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/cloud-platform
 30        ```
 31        """
 32        if service_account_info:
 33            self.gc = gspread.service_account_from_dict(service_account_info)
 34        else:
 35            # This assumes gcloud auth application-default login has been run
 36            creds, _ = default(scopes=self._SCOPES)
 37            creds.refresh(Request())
 38            self.gc = gspread.Client(auth=creds)
 39
 40    def _open_worksheet(self, spreadsheet_id: str, worksheet_name: str) -> gspread.Worksheet:
 41        """
 42        Open a spreadsheet by its ID.
 43
 44        **Args:**
 45        - spreadsheet_id (str): The ID of the Google Sheet.
 46        """
 47        spreadsheet = self.gc.open_by_key(spreadsheet_id)
 48        return spreadsheet.worksheet(worksheet_name)
 49
 50    def update_cell(self, spreadsheet_id: str, worksheet_name: str, cell: str, value: str) -> None:
 51        """
 52        Update a specific cell in the sheet.
 53
 54        **Args:**
 55        - spreadsheet_id (str): Spreadsheet ID.
 56        - worksheet_name (str): Sheet/tab name.
 57        - cell (str): A1-style cell notation.
 58        - value (str): Value to insert.
 59        """
 60        worksheet = self._open_worksheet(spreadsheet_id, worksheet_name)
 61        worksheet.update([[value]], range_name=cell)
 62
 63    def get_cell_value(self, spreadsheet_id: str, worksheet_name: str, cell: str) -> str:
 64        """
 65        Get the value of a specific cell.
 66
 67        **Args:**
 68        - spreadsheet_id (str): Spreadsheet ID.
 69        - worksheet_name (str): Sheet/tab name.
 70        - cell (str): A1-style cell reference.
 71
 72        **Returns:**
 73        - str or None: Cell value or None if empty.
 74        """
 75        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
 76        return ws.acell(cell).value
 77
 78    def get_last_row(self, spreadsheet_id: str, worksheet_name: str) -> int:
 79        """
 80        Get the last non-empty row in the specified column, accounting for trailing empty rows.
 81
 82        **Args:**
 83        - spreadsheet_id (str): Spreadsheet ID.
 84        - worksheet_name (str): Sheet/tab name.
 85
 86        **Returns:**
 87        - int: The last non-empty row number.
 88        """
 89        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
 90        col_values = ws.col_values(1)  # Get all values in the first column
 91        for row_index in range(len(col_values), 0, -1):  # Iterate from the last row to the first
 92            if col_values[row_index - 1]:  # Check if the cell is not empty
 93                return row_index
 94        return 0  # Return 0 if all rows are empty
 95
 96    def get_column_values(self, spreadsheet_id: str, worksheet_name: str, column: str) -> list:
 97        """
 98        Get all values in a specific column in order of row.
 99
100        **Args:**
101        - spreadsheet_id (str): Spreadsheet ID.
102        - worksheet_name (str): Sheet/tab name.
103        - column (str): Column identifier (e.g., "A" or "1").
104
105        **Returns:**
106        - list: List of values in the column.
107        """
108        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
109
110        # Convert column letter to number if it's a letter
111        if column.isalpha():
112            # gspread uses 1-based indexing for columns
113            column_index = 0
114            for char in column.upper():
115                column_index = column_index * 26 + (ord(char) - ord('A') + 1)
116        else:
117            # If column is already a number
118            column_index = int(column)
119
120        return ws.col_values(column_index)
121
122    def get_worksheet_as_dict(self, spreadsheet_id: str, worksheet_name: str) -> list[dict]:
123        """
124        Get all data from a worksheet as a list of dictionaries.
125
126        The first row is used as the header/keys for the dictionaries.
127        Each subsequent row becomes a dictionary with the header values as keys.
128
129        **Args:**
130        - spreadsheet_id (str): Spreadsheet ID.
131        - worksheet_name (str): Sheet/tab name.
132
133        **Returns:**
134        - list[dict]: List of dictionaries, where each dictionary represents a row
135                      with column headers as keys.
136        """
137        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
138        return ws.get_all_records()
139
140    def batch_update_cells(self, spreadsheet_id: str, worksheet_name: str, updates: list[dict[str, str]]) -> None:
141        """
142        Update multiple cells in a single batch request.
143
144        This method is more efficient than calling update_cell multiple times
145        as it sends all updates in a single API request.
146
147        **Args:**
148        - spreadsheet_id (str): Spreadsheet ID.
149        - worksheet_name (str): Sheet/tab name.
150        - updates (list[dict[str, str]]): List of dictionaries, each containing:
151            - 'cell': A1-style cell notation (e.g., 'A1', 'B5')
152            - 'value': Value to insert
153
154        **Example:**
155        ```python
156        updates = [
157            {'cell': 'A1', 'value': 'Name'},
158            {'cell': 'B1', 'value': 'Age'},
159            {'cell': 'A2', 'value': 'John'},
160            {'cell': 'B2', 'value': '30'}
161        ]
162        gs.batch_update_cells(spreadsheet_id, worksheet_name, updates)
163        ```
164        """
165        worksheet = self._open_worksheet(spreadsheet_id, worksheet_name)
166
167        # Build the batch update data
168        batch_data = []
169        for update in updates:
170            batch_data.append({
171                'range': update['cell'],
172                'values': [[update['value']]]
173            })
174
175        # Perform batch update
176        worksheet.batch_update(batch_data)

Class to interact with Google Sheets API.

GoogleSheets(service_account_info: Optional[dict] = None)
14    def __init__(self, service_account_info: Optional[dict] = None):
15        """
16        Initialize the GoogleSheets instance using the service account or user credentials.
17
18        This method sets up the Google Sheets client using either the provided service account
19        credentials or the application-default credentials. If no service account information
20        is provided, ensure that the application-default credentials are properly configured.
21
22        **Args:**
23        - service_account_info (Optional[dict]): A dictionary containing the service account credentials.
24
25        **Example:**
26        To use application-default credentials, run the following command:
27        ```
28        gcloud auth application-default login \
29        --scopes=https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/cloud-platform
30        ```
31        """
32        if service_account_info:
33            self.gc = gspread.service_account_from_dict(service_account_info)
34        else:
35            # This assumes gcloud auth application-default login has been run
36            creds, _ = default(scopes=self._SCOPES)
37            creds.refresh(Request())
38            self.gc = gspread.Client(auth=creds)

Initialize the GoogleSheets instance using the service account or user credentials.

This method sets up the Google Sheets client using either the provided service account credentials or the application-default credentials. If no service account information is provided, ensure that the application-default credentials are properly configured.

Args:

  • service_account_info (Optional[dict]): A dictionary containing the service account credentials.

Example: To use application-default credentials, run the following command:

gcloud auth application-default login         --scopes=https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/cloud-platform
def update_cell( self, spreadsheet_id: str, worksheet_name: str, cell: str, value: str) -> None:
50    def update_cell(self, spreadsheet_id: str, worksheet_name: str, cell: str, value: str) -> None:
51        """
52        Update a specific cell in the sheet.
53
54        **Args:**
55        - spreadsheet_id (str): Spreadsheet ID.
56        - worksheet_name (str): Sheet/tab name.
57        - cell (str): A1-style cell notation.
58        - value (str): Value to insert.
59        """
60        worksheet = self._open_worksheet(spreadsheet_id, worksheet_name)
61        worksheet.update([[value]], range_name=cell)

Update a specific cell in the sheet.

Args:

  • spreadsheet_id (str): Spreadsheet ID.
  • worksheet_name (str): Sheet/tab name.
  • cell (str): A1-style cell notation.
  • value (str): Value to insert.
def get_cell_value(self, spreadsheet_id: str, worksheet_name: str, cell: str) -> str:
63    def get_cell_value(self, spreadsheet_id: str, worksheet_name: str, cell: str) -> str:
64        """
65        Get the value of a specific cell.
66
67        **Args:**
68        - spreadsheet_id (str): Spreadsheet ID.
69        - worksheet_name (str): Sheet/tab name.
70        - cell (str): A1-style cell reference.
71
72        **Returns:**
73        - str or None: Cell value or None if empty.
74        """
75        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
76        return ws.acell(cell).value

Get the value of a specific cell.

Args:

  • spreadsheet_id (str): Spreadsheet ID.
  • worksheet_name (str): Sheet/tab name.
  • cell (str): A1-style cell reference.

Returns:

  • str or None: Cell value or None if empty.
def get_last_row(self, spreadsheet_id: str, worksheet_name: str) -> int:
78    def get_last_row(self, spreadsheet_id: str, worksheet_name: str) -> int:
79        """
80        Get the last non-empty row in the specified column, accounting for trailing empty rows.
81
82        **Args:**
83        - spreadsheet_id (str): Spreadsheet ID.
84        - worksheet_name (str): Sheet/tab name.
85
86        **Returns:**
87        - int: The last non-empty row number.
88        """
89        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
90        col_values = ws.col_values(1)  # Get all values in the first column
91        for row_index in range(len(col_values), 0, -1):  # Iterate from the last row to the first
92            if col_values[row_index - 1]:  # Check if the cell is not empty
93                return row_index
94        return 0  # Return 0 if all rows are empty

Get the last non-empty row in the specified column, accounting for trailing empty rows.

Args:

  • spreadsheet_id (str): Spreadsheet ID.
  • worksheet_name (str): Sheet/tab name.

Returns:

  • int: The last non-empty row number.
def get_column_values(self, spreadsheet_id: str, worksheet_name: str, column: str) -> list:
 96    def get_column_values(self, spreadsheet_id: str, worksheet_name: str, column: str) -> list:
 97        """
 98        Get all values in a specific column in order of row.
 99
100        **Args:**
101        - spreadsheet_id (str): Spreadsheet ID.
102        - worksheet_name (str): Sheet/tab name.
103        - column (str): Column identifier (e.g., "A" or "1").
104
105        **Returns:**
106        - list: List of values in the column.
107        """
108        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
109
110        # Convert column letter to number if it's a letter
111        if column.isalpha():
112            # gspread uses 1-based indexing for columns
113            column_index = 0
114            for char in column.upper():
115                column_index = column_index * 26 + (ord(char) - ord('A') + 1)
116        else:
117            # If column is already a number
118            column_index = int(column)
119
120        return ws.col_values(column_index)

Get all values in a specific column in order of row.

Args:

  • spreadsheet_id (str): Spreadsheet ID.
  • worksheet_name (str): Sheet/tab name.
  • column (str): Column identifier (e.g., "A" or "1").

Returns:

  • list: List of values in the column.
def get_worksheet_as_dict(self, spreadsheet_id: str, worksheet_name: str) -> list[dict]:
122    def get_worksheet_as_dict(self, spreadsheet_id: str, worksheet_name: str) -> list[dict]:
123        """
124        Get all data from a worksheet as a list of dictionaries.
125
126        The first row is used as the header/keys for the dictionaries.
127        Each subsequent row becomes a dictionary with the header values as keys.
128
129        **Args:**
130        - spreadsheet_id (str): Spreadsheet ID.
131        - worksheet_name (str): Sheet/tab name.
132
133        **Returns:**
134        - list[dict]: List of dictionaries, where each dictionary represents a row
135                      with column headers as keys.
136        """
137        ws = self._open_worksheet(spreadsheet_id, worksheet_name)
138        return ws.get_all_records()

Get all data from a worksheet as a list of dictionaries.

The first row is used as the header/keys for the dictionaries. Each subsequent row becomes a dictionary with the header values as keys.

Args:

  • spreadsheet_id (str): Spreadsheet ID.
  • worksheet_name (str): Sheet/tab name.

Returns:

  • list[dict]: List of dictionaries, where each dictionary represents a row with column headers as keys.
def batch_update_cells( self, spreadsheet_id: str, worksheet_name: str, updates: list[dict[str, str]]) -> None:
140    def batch_update_cells(self, spreadsheet_id: str, worksheet_name: str, updates: list[dict[str, str]]) -> None:
141        """
142        Update multiple cells in a single batch request.
143
144        This method is more efficient than calling update_cell multiple times
145        as it sends all updates in a single API request.
146
147        **Args:**
148        - spreadsheet_id (str): Spreadsheet ID.
149        - worksheet_name (str): Sheet/tab name.
150        - updates (list[dict[str, str]]): List of dictionaries, each containing:
151            - 'cell': A1-style cell notation (e.g., 'A1', 'B5')
152            - 'value': Value to insert
153
154        **Example:**
155        ```python
156        updates = [
157            {'cell': 'A1', 'value': 'Name'},
158            {'cell': 'B1', 'value': 'Age'},
159            {'cell': 'A2', 'value': 'John'},
160            {'cell': 'B2', 'value': '30'}
161        ]
162        gs.batch_update_cells(spreadsheet_id, worksheet_name, updates)
163        ```
164        """
165        worksheet = self._open_worksheet(spreadsheet_id, worksheet_name)
166
167        # Build the batch update data
168        batch_data = []
169        for update in updates:
170            batch_data.append({
171                'range': update['cell'],
172                'values': [[update['value']]]
173            })
174
175        # Perform batch update
176        worksheet.batch_update(batch_data)

Update multiple cells in a single batch request.

This method is more efficient than calling update_cell multiple times as it sends all updates in a single API request.

Args:

  • spreadsheet_id (str): Spreadsheet ID.
  • worksheet_name (str): Sheet/tab name.
  • updates (list[dict[str, str]]): List of dictionaries, each containing:
    • 'cell': A1-style cell notation (e.g., 'A1', 'B5')
    • 'value': Value to insert

Example:

updates = [
    {'cell': 'A1', 'value': 'Name'},
    {'cell': 'B1', 'value': 'Age'},
    {'cell': 'A2', 'value': 'John'},
    {'cell': 'B2', 'value': '30'}
]
gs.batch_update_cells(spreadsheet_id, worksheet_name, updates)