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]]:
|
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}"
|
url = f"https://docs.google.com/spreadsheets/d/{WORKBOOK_ID}/gviz/tq?{params}"
|
||||||
response = requests.get(url, timeout=30)
|
response = requests.get(url, timeout=30)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
@@ -50,32 +50,56 @@ def read_gviz(sheet: str) -> list[list[str]]:
|
|||||||
text = text[:-1].rstrip()
|
text = text[:-1].rstrip()
|
||||||
if not text.startswith(GVIZ_PREFIX) or not text.endswith(")"):
|
if not text.startswith(GVIZ_PREFIX) or not text.endswith(")"):
|
||||||
raise ValueError(f"invalid gviz response for worksheet {sheet!r}")
|
raise ValueError(f"invalid gviz response for worksheet {sheet!r}")
|
||||||
payload = json.loads(text[len(GVIZ_PREFIX) : -1])
|
try:
|
||||||
if payload.get("status") != "ok":
|
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}")
|
raise ValueError(f"gviz query failed for worksheet {sheet!r}")
|
||||||
table = payload.get("table", {})
|
table = payload.get("table")
|
||||||
columns = table.get("cols", [])
|
if not isinstance(table, dict):
|
||||||
rows = table.get("rows", [])
|
raise ValueError(f"invalid gviz table for worksheet {sheet!r}")
|
||||||
width = len(columns)
|
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:
|
def cell_value(cell: dict | None) -> str:
|
||||||
if not cell:
|
if cell is None:
|
||||||
return ""
|
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:
|
if value is None:
|
||||||
return ""
|
return ""
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
return "TRUE" if value else "FALSE"
|
return "TRUE" if value else "FALSE"
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
result: list[list[str]] = []
|
data_rows: list[list[str]] = []
|
||||||
for row in rows:
|
for row in rows:
|
||||||
cells = row.get("c", [])
|
if not isinstance(row, dict) or not isinstance(row.get("c", []), list):
|
||||||
if len(cells) > width:
|
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}")
|
raise ValueError(f"gviz row is wider than columns in worksheet {sheet!r}")
|
||||||
values = [cell_value(cell) for cell in cells]
|
values = [cell_value(cell) for cell in cells]
|
||||||
result.append(values + [""] * (width - len(values)))
|
data_rows.append((values + [""] * (len(columns) - len(values)))[: len(headers)])
|
||||||
return result
|
return [headers] + data_rows
|
||||||
|
|
||||||
|
|
||||||
def normalize_sheet(name: str, rows: list[list[str]]) -> tuple[list[str], list[list[str]]]:
|
def normalize_sheet(name: str, rows: list[list[str]]) -> tuple[list[str], list[list[str]]]:
|
||||||
|
|||||||
Reference in New Issue
Block a user