Read gviz column labels as headers

This commit is contained in:
liuji
2026-10-10 15:08:53 +08:00
parent 28f67d89a6
commit d84064ba84
+38 -14
View File
@@ -39,7 +39,7 @@ def sheet_names(xlsx: bytes) -> list[str]:
def read_gviz(sheet: str) -> list[list[str]]:
params = urlencode({"sheet": sheet, "headers": "0"})
params = urlencode({"sheet": sheet})
url = f"https://docs.google.com/spreadsheets/d/{WORKBOOK_ID}/gviz/tq?{params}"
response = requests.get(url, timeout=30)
response.raise_for_status()
@@ -50,32 +50,56 @@ def read_gviz(sheet: str) -> list[list[str]]:
text = text[:-1].rstrip()
if not text.startswith(GVIZ_PREFIX) or not text.endswith(")"):
raise ValueError(f"invalid gviz response for worksheet {sheet!r}")
payload = json.loads(text[len(GVIZ_PREFIX) : -1])
if payload.get("status") != "ok":
try:
payload = json.loads(text[len(GVIZ_PREFIX) : -1])
except json.JSONDecodeError as error:
raise ValueError(f"invalid gviz response for worksheet {sheet!r}") from error
if not isinstance(payload, dict) or payload.get("status") != "ok":
raise ValueError(f"gviz query failed for worksheet {sheet!r}")
table = payload.get("table", {})
columns = table.get("cols", [])
rows = table.get("rows", [])
width = len(columns)
table = payload.get("table")
if not isinstance(table, dict):
raise ValueError(f"invalid gviz table for worksheet {sheet!r}")
columns = table.get("cols")
rows = table.get("rows")
if not isinstance(columns, list) or not isinstance(rows, list):
raise ValueError(f"invalid gviz table for worksheet {sheet!r}")
headers: list[str] = []
for column in columns:
if not isinstance(column, dict):
raise ValueError(f"invalid gviz column for worksheet {sheet!r}")
label = column.get("label")
if label is not None and not isinstance(label, str):
raise ValueError(f"invalid gviz column label for worksheet {sheet!r}")
headers.append(label or "")
while headers and headers[-1] == "":
headers.pop()
if not headers:
return []
def cell_value(cell: dict | None) -> str:
if not cell:
if cell is None:
return ""
value = cell.get("f") if cell.get("f") is not None else cell.get("v", "")
if not isinstance(cell, dict):
raise ValueError(f"invalid gviz cell for worksheet {sheet!r}")
value = cell.get("f") if cell.get("f") is not None else cell.get("v")
if value is None:
return ""
if isinstance(value, bool):
return "TRUE" if value else "FALSE"
return str(value)
result: list[list[str]] = []
data_rows: list[list[str]] = []
for row in rows:
cells = row.get("c", [])
if len(cells) > width:
if not isinstance(row, dict) or not isinstance(row.get("c", []), list):
raise ValueError(f"invalid gviz row for worksheet {sheet!r}")
cells = row["c"]
if len(cells) > len(columns):
raise ValueError(f"gviz row is wider than columns in worksheet {sheet!r}")
values = [cell_value(cell) for cell in cells]
result.append(values + [""] * (width - len(values)))
return result
data_rows.append((values + [""] * (len(columns) - len(values)))[: len(headers)])
return [headers] + data_rows
def normalize_sheet(name: str, rows: list[list[str]]) -> tuple[list[str], list[list[str]]]: