From 1689aa07236367bd4d4011e2355d2d0ee7a501ec Mon Sep 17 00:00:00 2001 From: Ivor Strelar Date: Tue, 30 Jun 2026 14:15:07 +0200 Subject: [PATCH] feat: use column letters (A, B, C...) in Google Sheet nodes --- py/sf_google_sheet_cell.py | 33 +++++++++++++++++++-------------- py/sf_google_sheet_range.py | 37 ++++++++++++++++++++----------------- pyproject.toml | 2 +- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/py/sf_google_sheet_cell.py b/py/sf_google_sheet_cell.py index ddad351..0a08262 100644 --- a/py/sf_google_sheet_cell.py +++ b/py/sf_google_sheet_cell.py @@ -4,10 +4,21 @@ import re import requests +def col_letter_to_index(col_str: str) -> int: + """Convert a column letter (A, B, C, ... Z, AA, AB, ...) to a 0-based index.""" + col_str = col_str.strip().upper() + if not col_str or not col_str.isalpha(): + raise ValueError(f"Invalid column label '{col_str}'. Use letters like A, B, C, AA, AB, etc.") + result = 0 + for char in col_str: + result = result * 26 + (ord(char) - ord("A") + 1) + return result - 1 + + class SFGoogleSheetCell: """ Reads a single cell from a publicly shared Google Sheet. - Row and column are 1-indexed to match Google Sheets display numbers. + Row is 1-indexed. Column uses letter notation (A, B, C, ...) matching Google Sheets. The sheet must be shared as "Anyone with the link can view." """ @@ -47,12 +58,10 @@ class SFGoogleSheetCell: }, ), "col": ( - "INT", + "STRING", { - "default": 1, - "min": 1, - "step": 1, - "tooltip": "Column number starting from 1 (A=1, B=2, C=3, etc.).", + "default": "A", + "tooltip": "Column letter as shown in Google Sheets (A, B, C, ... Z, AA, AB, ...).", }, ), } @@ -63,11 +72,11 @@ class SFGoogleSheetCell: FUNCTION = "get_cell" CATEGORY = "Stillfront/Utils" - def get_cell(self, url: str, sheet_gid: str, row: int, col: int) -> tuple: + def get_cell(self, url: str, sheet_gid: str, row: int, col: str) -> tuple: spreadsheet_id = self._extract_id(url) gid = sheet_gid.strip() or "0" + col_idx = col_letter_to_index(col) - # gviz/tq endpoint correctly handles any sheet GID, unlike export?format=csv csv_url = ( f"https://docs.google.com/spreadsheets/d/{spreadsheet_id}" f"/gviz/tq?tqx=out:csv&gid={gid}" @@ -87,19 +96,15 @@ class SFGoogleSheetCell: content = response.content.decode("utf-8") table = list(csv.reader(io.StringIO(content))) - # Convert 1-indexed user input to 0-indexed Python list access row_idx = row - 1 - col_idx = col - 1 if row_idx >= len(table): - raise ValueError( - f"Row {row} is out of range — the sheet has {len(table)} rows." - ) + raise ValueError(f"Row {row} is out of range — the sheet has {len(table)} rows.") row_data = table[row_idx] if col_idx >= len(row_data): raise ValueError( - f"Column {col} is out of range — row {row} has {len(row_data)} columns." + f"Column '{col.upper()}' is out of range — row {row} has {len(row_data)} columns." ) return (str(row_data[col_idx]),) diff --git a/py/sf_google_sheet_range.py b/py/sf_google_sheet_range.py index e05caef..6cb39c7 100644 --- a/py/sf_google_sheet_range.py +++ b/py/sf_google_sheet_range.py @@ -3,13 +3,15 @@ import io import re import requests +from .sf_google_sheet_cell import col_letter_to_index + class SFGoogleSheetRange: """ Reads a range of cells from a publicly shared Google Sheet and joins them into a single string using a configurable delimiter. Cells are read left to right, top to bottom. - Row and column numbers are 1-indexed to match Google Sheets display numbers. + Row numbers are 1-indexed. Columns use letter notation (A, B, C, ...) matching Google Sheets. The sheet must be shared as "Anyone with the link can view." """ @@ -48,12 +50,10 @@ class SFGoogleSheetRange: }, ), "start_col": ( - "INT", + "STRING", { - "default": 1, - "min": 1, - "step": 1, - "tooltip": "First column of the range (1 = column A).", + "default": "A", + "tooltip": "First column of the range (e.g. A, B, C, AA, ...).", }, ), "end_row": ( @@ -66,12 +66,10 @@ class SFGoogleSheetRange: }, ), "end_col": ( - "INT", + "STRING", { - "default": 1, - "min": 1, - "step": 1, - "tooltip": "Last column of the range (inclusive).", + "default": "A", + "tooltip": "Last column of the range (inclusive, e.g. A, B, C, AA, ...).", }, ), "delimiter": ( @@ -98,15 +96,20 @@ class SFGoogleSheetRange: url: str, sheet_gid: str, start_row: int, - start_col: int, + start_col: str, end_row: int, - end_col: int, + end_col: str, delimiter: str, ) -> tuple: + start_col_idx = col_letter_to_index(start_col) + end_col_idx = col_letter_to_index(end_col) + if start_row > end_row: - raise ValueError(f"start_row ({start_row}) must be less than or equal to end_row ({end_row}).") - if start_col > end_col: - raise ValueError(f"start_col ({start_col}) must be less than or equal to end_col ({end_col}).") + raise ValueError(f"start_row ({start_row}) must be ≤ end_row ({end_row}).") + if start_col_idx > end_col_idx: + raise ValueError( + f"start_col '{start_col.upper()}' must be ≤ end_col '{end_col.upper()}'." + ) table = self._fetch_sheet(url, sheet_gid) @@ -115,7 +118,7 @@ class SFGoogleSheetRange: if r >= len(table): break row_data = table[r] - for c in range(start_col - 1, end_col): + for c in range(start_col_idx, end_col_idx + 1): value = row_data[c] if c < len(row_data) else "" cells.append(str(value)) diff --git a/pyproject.toml b/pyproject.toml index a8d7a99..cc742d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-sf-nodes" description = "Custom nodes for ComfyUI including LLM Chat with support for Claude, Gemini, and OpenAI models, WaveSpeed AI image/video generation, VertexAI integration, and workflow utilities" -version = "1.0.4" +version = "1.0.5" license = { file = "LICENSE" } dependencies = [ "requests>=2.31.0",