Read gviz column labels as headers
This commit is contained in:
@@ -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]]]:
|
||||
|
||||
Reference in New Issue
Block a user