feat: use column letters (A, B, C...) in Google Sheet nodes
This commit is contained in:
+19
-14
@@ -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]),)
|
||||
|
||||
+20
-17
@@ -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))
|
||||
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user