eftoolkit.gsheets¶
Google Sheets client with automatic batching and dashboard orchestration.
Classes¶
Spreadsheet¶
Spreadsheet
¶
Spreadsheet(
credentials: dict | None = None,
spreadsheet_name: str = '',
*,
max_retries: int = 5,
base_delay: float = 2.0,
local_preview: bool = False,
preview_dir: str | Path = 'gsheets_preview',
)
Google Spreadsheet client for managing worksheets.
Represents the entire spreadsheet document. Use worksheet() to get individual tabs for read/write operations.
Can be used as a context manager to automatically flush all accessed worksheets on exit. In local_preview mode, previews open in browser:
with Spreadsheet(credentials, 'My Sheet') as ss:
ws1 = ss.worksheet('Tab1')
ws1.write_dataframe(df1)
ws2 = ss.worksheet('Tab2')
ws2.write_dataframe(df2)
# Both ws1 and ws2 are flushed here
# In local_preview mode, browser tabs open automatically
Initialize Spreadsheet client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
credentials
|
dict | None
|
Service account credentials dict. Required unless local_preview=True. |
None
|
spreadsheet_name
|
str
|
Name of the spreadsheet to open. |
''
|
max_retries
|
int
|
Max retry attempts for API errors (429, 5xx). |
5
|
base_delay
|
float
|
Base delay for exponential backoff. |
2.0
|
local_preview
|
bool
|
If True, skip API calls and render to local HTML. |
False
|
preview_dir
|
str | Path
|
Directory for HTML preview files (only used if local_preview=True). |
'gsheets_preview'
|
Source code in eftoolkit/gsheets/sheet.py
worksheet
¶
worksheet(name: str) -> Worksheet
Get worksheet by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Worksheet title (tab name). |
required |
Returns:
| Type | Description |
|---|---|
Worksheet
|
Worksheet instance for the specified tab. |
Raises:
| Type | Description |
|---|---|
WorksheetNotFound
|
If worksheet doesn't exist (not in local_preview mode). |
Source code in eftoolkit/gsheets/sheet.py
get_worksheet_names
¶
List all worksheet names.
Returns:
| Type | Description |
|---|---|
list[str]
|
List of worksheet titles. |
Source code in eftoolkit/gsheets/sheet.py
create_worksheet
¶
create_worksheet(
name: str,
rows: int = 1000,
cols: int = 26,
*,
replace: bool = False,
) -> Worksheet
Create a new worksheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Title for the new worksheet. |
required |
rows
|
int
|
Number of rows (default 1000). |
1000
|
cols
|
int
|
Number of columns (default 26). |
26
|
replace
|
bool
|
If True, delete existing worksheet with same name first. |
False
|
Returns:
| Type | Description |
|---|---|
Worksheet
|
Worksheet instance for the new tab. |
Source code in eftoolkit/gsheets/sheet.py
delete_worksheet
¶
Delete worksheet by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Worksheet title to delete. |
required |
ignore_missing
|
bool
|
If True, don't raise if worksheet doesn't exist. |
True
|
Source code in eftoolkit/gsheets/sheet.py
reorder_worksheets
¶
Reorder worksheets (tabs) to the specified order.
Worksheets are reordered to match the given list. Worksheets not in the list are moved to the end in their original relative order. Worksheet names in the list that don't exist in the spreadsheet are skipped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
order
|
list[str]
|
List of worksheet titles in the desired order. |
required |
Example
ss.reorder_worksheets(['Dashboard', 'Draft', 'Manual Adds'])
Dashboard first, then Draft, then Manual Adds, then any other tabs¶
Source code in eftoolkit/gsheets/sheet.py
Worksheet¶
Worksheet
¶
Worksheet(
gspread_worksheet: Any,
spreadsheet: Spreadsheet,
*,
local_preview: bool = False,
preview_output: Path | None = None,
worksheet_name: str | None = None,
)
A single worksheet (tab) within a Google Spreadsheet.
Handles all read/write/format operations for one tab. Operations are queued and flushed via flush() or context manager exit.
Initialize worksheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gspread_worksheet
|
Any
|
The underlying gspread Worksheet object. |
required |
spreadsheet
|
Spreadsheet
|
Parent Spreadsheet instance. |
required |
local_preview
|
bool
|
If True, skip API calls and render to local HTML. |
False
|
preview_output
|
Path | None
|
Path for HTML preview file. |
None
|
worksheet_name
|
str | None
|
Worksheet name (used in local_preview mode). |
None
|
Source code in eftoolkit/gsheets/sheet.py
read
¶
Read worksheet to DataFrame (first row = headers).
Source code in eftoolkit/gsheets/sheet.py
write_dataframe
¶
write_dataframe(
df: DataFrame,
location: str = 'A1',
*,
include_header: bool = True,
format_dict: dict[str, Any] | None = None,
) -> None
Queue DataFrame write with optional formatting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
DataFrame to write. |
required |
location
|
str
|
Cell location to start writing (e.g., 'A1'). |
'A1'
|
include_header
|
bool
|
If True, include column names as first row. |
True
|
format_dict
|
dict[str, Any] | None
|
Optional dict mapping range names to format dicts. |
None
|
Source code in eftoolkit/gsheets/sheet.py
write_values
¶
Queue cell values update.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range (e.g., 'A1:B2'). |
required |
values
|
list[list[Any]]
|
2D list of values to write. |
required |
Source code in eftoolkit/gsheets/sheet.py
format_range
¶
Queue cell formatting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range. |
required |
format_dict
|
dict[str, Any]
|
Format specification dict. |
required |
Source code in eftoolkit/gsheets/sheet.py
set_borders
¶
Queue border formatting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range. |
required |
borders
|
dict[str, Any]
|
Border specification dict. |
required |
Source code in eftoolkit/gsheets/sheet.py
set_column_width
¶
Queue column width update.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
column
|
str | int
|
Column letter or 1-based index. |
required |
width
|
int
|
Width in pixels. |
required |
Source code in eftoolkit/gsheets/sheet.py
auto_resize_columns
¶
Queue column auto-resize.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_col
|
int
|
1-based start column index. |
required |
end_col
|
int
|
1-based end column index. |
required |
Source code in eftoolkit/gsheets/sheet.py
set_notes
¶
Queue cell notes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
notes
|
dict[str, str]
|
Dict mapping cell references to note text. |
required |
Source code in eftoolkit/gsheets/sheet.py
merge_cells
¶
Queue cell merge.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range to merge (e.g., 'A1:C1'). |
required |
merge_type
|
str
|
One of 'MERGE_ALL', 'MERGE_COLUMNS', 'MERGE_ROWS'. |
'MERGE_ALL'
|
Source code in eftoolkit/gsheets/sheet.py
unmerge_cells
¶
Queue cell unmerge.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range to unmerge. |
required |
Source code in eftoolkit/gsheets/sheet.py
sort_range
¶
Queue range sort.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range to sort. |
required |
sort_specs
|
list[dict[str, Any]]
|
List of sort specifications. Each spec should have: - 'column': 0-based column index within the range - 'ascending': True for ascending, False for descending (default True) |
required |
Example
ws.sort_range('A1:C10', [{'column': 0, 'ascending': True}])
Source code in eftoolkit/gsheets/sheet.py
set_data_validation
¶
Queue data validation rule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range for validation. |
required |
rule
|
dict[str, Any]
|
Validation rule dict. Common keys: - 'type': 'ONE_OF_LIST', 'ONE_OF_RANGE', 'NUMBER_BETWEEN', etc. - 'values': List of allowed values (for ONE_OF_LIST) - 'showDropdown': True to show dropdown (default True) - 'strict': True to reject invalid input (default True) |
required |
Example
ws.set_data_validation('A1:A10', { 'type': 'ONE_OF_LIST', 'values': ['Yes', 'No', 'Maybe'], 'showDropdown': True, })
Source code in eftoolkit/gsheets/sheet.py
clear_data_validation
¶
Queue removal of data validation rules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range to clear validation from. |
required |
Source code in eftoolkit/gsheets/sheet.py
add_conditional_format
¶
Queue conditional formatting rule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_name
|
str
|
A1 notation range for conditional format. |
required |
rule
|
dict[str, Any]
|
Conditional format rule dict. Should contain: - 'type': 'CUSTOM_FORMULA', 'NUMBER_GREATER', 'TEXT_CONTAINS', etc. - 'values': Condition values (e.g., formula string) - 'format': Cell format to apply when condition is met |
required |
Example
ws.add_conditional_format('A1:A10', { 'type': 'CUSTOM_FORMULA', 'values': ['=A1>100'], 'format': {'backgroundColor': {'red': 1, 'green': 0, 'blue': 0}}, })
Source code in eftoolkit/gsheets/sheet.py
insert_rows
¶
Queue row insertion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_row
|
int
|
1-based row index where new rows will be inserted. |
required |
num_rows
|
int
|
Number of rows to insert (default 1). |
1
|
Source code in eftoolkit/gsheets/sheet.py
delete_rows
¶
Queue row deletion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_row
|
int
|
1-based row index of first row to delete. |
required |
num_rows
|
int
|
Number of rows to delete (default 1). |
1
|
Source code in eftoolkit/gsheets/sheet.py
insert_columns
¶
Queue column insertion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_col
|
int
|
1-based column index where new columns will be inserted. |
required |
num_cols
|
int
|
Number of columns to insert (default 1). |
1
|
Source code in eftoolkit/gsheets/sheet.py
delete_columns
¶
Queue column deletion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_col
|
int
|
1-based column index of first column to delete. |
required |
num_cols
|
int
|
Number of columns to delete (default 1). |
1
|
Source code in eftoolkit/gsheets/sheet.py
freeze_rows
¶
Queue freezing rows at the top of the worksheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_rows
|
int
|
Number of rows to freeze (0 to unfreeze). |
required |
Source code in eftoolkit/gsheets/sheet.py
freeze_columns
¶
Queue freezing columns at the left of the worksheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_cols
|
int
|
Number of columns to freeze (0 to unfreeze). |
required |
Source code in eftoolkit/gsheets/sheet.py
add_raw_request
¶
Queue a raw batchUpdate request.
Use this for operations not covered by other methods. The request will be passed directly to the Google Sheets batchUpdate API.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
dict[str, Any]
|
A single batchUpdate request dict. See Google Sheets API documentation for available request types: https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request |
required |
Example
Add a named range¶
ws.add_raw_request({ 'addNamedRange': { 'namedRange': { 'name': 'MyRange', 'range': { 'sheetId': 0, 'startRowIndex': 0, 'endRowIndex': 10, 'startColumnIndex': 0, 'endColumnIndex': 5, } } } })
Source code in eftoolkit/gsheets/sheet.py
flush
¶
Execute all queued operations.
In normal mode: sends batched API calls to Google Sheets. In local_preview mode: renders HTML.
Source code in eftoolkit/gsheets/sheet.py
open_preview
¶
Open the preview HTML in browser (local_preview mode only).
Source code in eftoolkit/gsheets/sheet.py
DashboardRunner¶
DashboardRunner
¶
DashboardRunner(
config: dict[str, Any],
credentials: dict[str, Any],
worksheets: list[WorksheetDefinition] | None = None,
*,
local_preview: bool = False,
)
Orchestrates the 6-phase sheet update workflow.
Executes worksheet generation, writing, and formatting in a structured sequence with logging and error handling.
Attributes:
| Name | Type | Description |
|---|---|---|
config |
Configuration dictionary passed to worksheet generate() methods. |
|
credentials |
Google service account credentials dictionary. |
|
context |
dict[str, Any]
|
Shared state dictionary populated during generation and available to subsequent worksheets. |
results |
dict[str, list[WorksheetAsset]]
|
Dictionary mapping worksheet names to their generated assets. |
Example
runner = DashboardRunner( ... config={'db': conn, 'sheet_name': 'Q1 Report'}, ... credentials=credentials, ... worksheets=[RevenueWorksheet(), ExpensesWorksheet()], ... ) runner.run()
Initialize DashboardRunner.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
dict[str, Any]
|
Configuration dictionary. Must include 'sheet_name' key. Passed to each worksheet's generate() method. |
required |
credentials
|
dict[str, Any]
|
Google service account credentials dictionary. |
required |
worksheets
|
list[WorksheetDefinition] | None
|
List of worksheet definitions to process. If None, uses worksheets from WorksheetRegistry. |
None
|
local_preview
|
bool
|
If True, render to local HTML instead of Google Sheets. |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If 'sheet_name' not in config. |
ValueError
|
If no worksheets provided and registry is empty. |
Source code in eftoolkit/gsheets/runner.py
run
¶
Execute the full 6-phase workflow.
Phases
- Validate structure
- Generate data
- Write data
- Apply formatting
- Run hooks
- Log summary
Raises:
| Type | Description |
|---|---|
Exception
|
Re-raises any exception from individual phases. |
Source code in eftoolkit/gsheets/runner.py
WorksheetRegistry¶
WorksheetRegistry
¶
Registry of worksheet definitions.
A thread-safe registry that maintains worksheet definitions in registration order.
Use reorder() to change the order after registration.
This class uses class-level state, so all operations are performed via classmethods.
Use clear() in tests to reset state between test cases.
Example
WorksheetRegistry.register([Summary(), Revenue(), Expenses()]) worksheets = WorksheetRegistry.get_ordered_worksheets() len(worksheets) 3
Or register one at a time:¶
WorksheetRegistry.register(SummaryWorksheet())
register
classmethod
¶
register(
worksheets: WorksheetDefinition
| list[WorksheetDefinition],
) -> None
Register one or more worksheet definitions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
worksheets
|
WorksheetDefinition | list[WorksheetDefinition]
|
A single worksheet definition or a list of definitions. Must implement the WorksheetDefinition protocol. Worksheets are appended in the order provided. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a worksheet with the same name is already registered. |
Example
WorksheetRegistry.register(SummaryWorksheet()) WorksheetRegistry.register([Revenue(), Expenses()])
Source code in eftoolkit/gsheets/registry.py
get_ordered_worksheets
classmethod
¶
get_ordered_worksheets() -> list[WorksheetDefinition]
Return worksheets in registration order.
Returns:
| Type | Description |
|---|---|
list[WorksheetDefinition]
|
List of worksheet definitions in the order they were registered, |
list[WorksheetDefinition]
|
respecting any reordering done via |
Example
worksheets = WorksheetRegistry.get_ordered_worksheets() for ws in worksheets: ... print(ws.name)
Source code in eftoolkit/gsheets/registry.py
get_worksheet
classmethod
¶
get_worksheet(name: str) -> WorksheetDefinition | None
Get a worksheet by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name of the worksheet to retrieve. |
required |
Returns:
| Type | Description |
|---|---|
WorksheetDefinition | None
|
The worksheet definition if found, None otherwise. |
Example
revenue = WorksheetRegistry.get_worksheet('Revenue') if revenue: ... assets = revenue.generate(config, context)
Source code in eftoolkit/gsheets/registry.py
reorder
classmethod
¶
Reorder worksheets to match the specified order.
Sets the worksheet order to match the provided list of names. All registered worksheet names must be included exactly once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
names
|
list[str]
|
List of worksheet names in the desired order. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If names don't match registered worksheets exactly (missing names, extra names, or duplicates). |
Example
WorksheetRegistry.register([Summary(), Revenue(), Expenses()]) WorksheetRegistry.reorder(["Expenses", "Summary", "Revenue"]) WorksheetRegistry.get_ordered_worksheets()[0].name "Expenses"
Source code in eftoolkit/gsheets/registry.py
clear
classmethod
¶
Clear all registered worksheets.
This method is primarily intended for use in tests to reset the registry state between test cases.
Example
WorksheetRegistry.clear() len(WorksheetRegistry.get_ordered_worksheets()) 0
Source code in eftoolkit/gsheets/registry.py
Types¶
CellLocation¶
CellLocation
dataclass
¶
Where a DataFrame should be written within a worksheet.
The worksheet name comes from the WorksheetDefinition.name property, so CellLocation only needs the cell address within that worksheet.
Attributes:
| Name | Type | Description |
|---|---|---|
cell |
str
|
The cell address where the DataFrame starts (e.g., 'B4', 'A1'). |
Example
location = CellLocation(cell='B4') location.cell 'B4'
WorksheetAsset¶
WorksheetAsset
dataclass
¶
WorksheetAsset(
df: DataFrame,
location: CellLocation,
format_config_path: Path | None = None,
format_dict: dict[str, Any] | None = None,
post_write_hooks: list[Callable] = list(),
)
An asset to be written to a worksheet.
Contains a DataFrame to write, its target location within the worksheet, optional formatting configuration, and post-write hooks.
A WorksheetDefinition.generate() returns a list of WorksheetAssets, allowing multiple DataFrames to be written to different locations on the same worksheet.
Attributes:
| Name | Type | Description |
|---|---|---|
df |
DataFrame
|
The DataFrame to write to the sheet. |
location |
CellLocation
|
Where to write the DataFrame within the worksheet. |
format_config_path |
Path | None
|
Path to a JSON format configuration file. |
format_dict |
dict[str, Any] | None
|
Inline format configuration dictionary. |
post_write_hooks |
list[Callable]
|
Callables to run after writing (e.g., conditional formatting). |
Example
asset = WorksheetAsset( ... df=my_dataframe, ... location=CellLocation(cell='B4'), ... format_config_path=Path('formats/summary.json'), ... )
WorksheetDefinition¶
WorksheetDefinition
¶
Bases: Protocol
Protocol for defining a worksheet in a spreadsheet.
Each WorksheetDefinition represents one worksheet in the spreadsheet. The generate() method returns a list of WorksheetAssets, allowing multiple DataFrames to be written to different locations on the same worksheet.
A worksheet definition specifies: - Worksheet name (becomes the tab name in the spreadsheet) - Data generation logic (can produce multiple DataFrames) - Format configuration for each DataFrame - Post-write hooks for conditional formatting, notes, merges, etc.
Example implementation
class RevenueWorksheet: @property def name(self) -> str: return 'Revenue' # Worksheet name
def generate(self, config: dict, context: dict) -> list[WorksheetAsset]:
summary_df = query_summary(config['db'])
breakdown_df = query_breakdown(config['db'])
return [
WorksheetAsset(
df=summary_df,
location=CellLocation(cell='B2'),
format_dict={'header_color': '#4a86e8'},
),
WorksheetAsset(
df=breakdown_df,
location=CellLocation(cell='B10'),
format_config_path=Path('formats/breakdown.json'),
),
]
def get_format_overrides(self, context: dict) -> dict:
return {'currency_format': '$#,##0.00'}
generate
¶
generate(
config: dict, context: dict
) -> list[WorksheetAsset]
Generate the worksheet's data and return a list of WorksheetAssets.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
dict
|
Configuration dictionary (e.g., database connections, settings). |
required |
context
|
dict
|
Runtime context (e.g., date ranges, user preferences). |
required |
Returns:
| Type | Description |
|---|---|
list[WorksheetAsset]
|
List of WorksheetAssets, each containing a DataFrame and its location |
list[WorksheetAsset]
|
within this worksheet. Multiple assets = multiple DataFrames on one worksheet. |
Source code in eftoolkit/gsheets/types.py
get_format_overrides
¶
Return format overrides for this worksheet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
dict
|
Runtime context for dynamic formatting decisions. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
Dictionary of format overrides to apply on top of base config. |