File size: 1,853 Bytes
2c88096
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
"""
sheets.py — pull a Google Sheet as XLSX so the existing importer can parse it.

We export the whole workbook (`export?format=xlsx`) and reuse
importer.milestones_from_xlsx, which already finds the "Brief" tab. This needs
the sheet to be shared as "Anyone with the link → Viewer" (no auth/keys).
"""
import re

import httpx

_ID_RE = re.compile(r"/spreadsheets/d/([a-zA-Z0-9-_]+)")


def extract_sheet_id(url_or_id: str) -> str:
    """Accept a full Google Sheets URL or a bare id; return the id."""
    s = (url_or_id or "").strip()
    if not s:
        raise ValueError("empty sheet URL")
    m = _ID_RE.search(s)
    if m:
        return m.group(1)
    # assume it's already an id if it has no slashes/spaces
    if "/" not in s and " " not in s:
        return s
    raise ValueError("could not find a spreadsheet id in that URL")


def export_url(sheet_id: str) -> str:
    return f"https://docs.google.com/spreadsheets/d/{sheet_id}/export?format=xlsx"


async def fetch_xlsx(url_or_id: str) -> bytes:
    """Download the workbook as XLSX bytes. Raises RuntimeError on access issues."""
    sheet_id = extract_sheet_id(url_or_id)
    url = export_url(sheet_id)
    async with httpx.AsyncClient(timeout=60.0, follow_redirects=True) as client:
        resp = await client.get(url)
    if resp.status_code == 200 and "spreadsheetml" in resp.headers.get("content-type", ""):
        return resp.content
    # Google returns an HTML login/permission page (often 200 or 401) when the
    # sheet isn't link-viewable.
    if resp.status_code in (401, 403) or "text/html" in resp.headers.get("content-type", ""):
        raise RuntimeError(
            "Could not read the sheet. Make sure it's shared as "
            "'Anyone with the link → Viewer'."
        )
    raise RuntimeError(f"Google Sheets export failed (HTTP {resp.status_code}).")