hyperlink support
This commit is contained in:
@@ -324,6 +324,44 @@ def _extract_cell_errors_from_grid(spreadsheet: dict) -> list[dict[str, Optional
|
||||
return errors
|
||||
|
||||
|
||||
def _extract_cell_hyperlinks_from_grid(spreadsheet: dict) -> list[dict[str, str]]:
|
||||
"""
|
||||
Extract hyperlink URLs from spreadsheet grid data.
|
||||
|
||||
Returns a list of dictionaries with:
|
||||
- "cell": cell A1 reference
|
||||
- "url": hyperlink URL
|
||||
"""
|
||||
hyperlinks: list[dict[str, str]] = []
|
||||
for sheet in spreadsheet.get("sheets", []) or []:
|
||||
sheet_title = sheet.get("properties", {}).get("title") or "Unknown"
|
||||
for grid in sheet.get("data", []) or []:
|
||||
start_row = _coerce_int(grid.get("startRow"), default=0)
|
||||
start_col = _coerce_int(grid.get("startColumn"), default=0)
|
||||
for row_offset, row_data in enumerate(grid.get("rowData", []) or []):
|
||||
if not row_data:
|
||||
continue
|
||||
for col_offset, cell_data in enumerate(
|
||||
row_data.get("values", []) or []
|
||||
):
|
||||
if not cell_data:
|
||||
continue
|
||||
hyperlink = cell_data.get("hyperlink")
|
||||
if not hyperlink:
|
||||
continue
|
||||
hyperlinks.append(
|
||||
{
|
||||
"cell": _format_a1_cell(
|
||||
sheet_title,
|
||||
start_row + row_offset,
|
||||
start_col + col_offset,
|
||||
),
|
||||
"url": hyperlink,
|
||||
}
|
||||
)
|
||||
return hyperlinks
|
||||
|
||||
|
||||
async def _fetch_detailed_sheet_errors(
|
||||
service, spreadsheet_id: str, a1_range: str
|
||||
) -> list[dict[str, Optional[str]]]:
|
||||
@@ -340,6 +378,22 @@ async def _fetch_detailed_sheet_errors(
|
||||
return _extract_cell_errors_from_grid(response)
|
||||
|
||||
|
||||
async def _fetch_sheet_hyperlinks(
|
||||
service, spreadsheet_id: str, a1_range: str
|
||||
) -> list[dict[str, str]]:
|
||||
response = await asyncio.to_thread(
|
||||
service.spreadsheets()
|
||||
.get(
|
||||
spreadsheetId=spreadsheet_id,
|
||||
ranges=[a1_range],
|
||||
includeGridData=True,
|
||||
fields="sheets(properties(title),data(startRow,startColumn,rowData(values(hyperlink))))",
|
||||
)
|
||||
.execute
|
||||
)
|
||||
return _extract_cell_hyperlinks_from_grid(response)
|
||||
|
||||
|
||||
def _format_sheet_error_section(
|
||||
*, errors: list[dict[str, Optional[str]]], range_label: str, max_details: int = 25
|
||||
) -> str:
|
||||
@@ -388,6 +442,29 @@ def _format_sheet_error_section(
|
||||
)
|
||||
|
||||
|
||||
def _format_sheet_hyperlink_section(
|
||||
*, hyperlinks: list[dict[str, str]], range_label: str, max_details: int = 25
|
||||
) -> str:
|
||||
"""
|
||||
Format a list of cell hyperlinks into a human-readable section.
|
||||
"""
|
||||
if not hyperlinks:
|
||||
return ""
|
||||
|
||||
lines = []
|
||||
for item in hyperlinks[:max_details]:
|
||||
cell = item.get("cell") or "(unknown cell)"
|
||||
url = item.get("url") or "(missing url)"
|
||||
lines.append(f"- {cell}: {url}")
|
||||
|
||||
suffix = (
|
||||
f"\n... and {len(hyperlinks) - max_details} more hyperlinks"
|
||||
if len(hyperlinks) > max_details
|
||||
else ""
|
||||
)
|
||||
return f"\n\nHyperlinks in range '{range_label}':\n" + "\n".join(lines) + suffix
|
||||
|
||||
|
||||
def _color_to_hex(color: Optional[dict]) -> Optional[str]:
|
||||
"""
|
||||
Convert a Sheets color object back to #RRGGBB hex string for display.
|
||||
|
||||
@@ -20,8 +20,10 @@ from gsheets.sheets_helpers import (
|
||||
_build_boolean_rule,
|
||||
_build_gradient_rule,
|
||||
_fetch_detailed_sheet_errors,
|
||||
_fetch_sheet_hyperlinks,
|
||||
_fetch_sheets_with_rules,
|
||||
_format_conditional_rules_section,
|
||||
_format_sheet_hyperlink_section,
|
||||
_format_sheet_error_section,
|
||||
_parse_a1_range,
|
||||
_parse_condition_values,
|
||||
@@ -201,10 +203,26 @@ async def read_sheet_values(
|
||||
if not values:
|
||||
return f"No data found in range '{range_name}' for {user_google_email}."
|
||||
|
||||
resolved_range = result.get("range", range_name)
|
||||
detailed_range = _a1_range_for_values(resolved_range, values) or resolved_range
|
||||
|
||||
hyperlink_section = ""
|
||||
try:
|
||||
hyperlinks = await _fetch_sheet_hyperlinks(
|
||||
service, spreadsheet_id, detailed_range
|
||||
)
|
||||
hyperlink_section = _format_sheet_hyperlink_section(
|
||||
hyperlinks=hyperlinks, range_label=detailed_range
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"[read_sheet_values] Failed fetching hyperlinks for range '%s': %s",
|
||||
detailed_range,
|
||||
exc,
|
||||
)
|
||||
|
||||
detailed_errors_section = ""
|
||||
if _values_contain_sheets_errors(values):
|
||||
resolved_range = result.get("range", range_name)
|
||||
detailed_range = _a1_range_for_values(resolved_range, values) or resolved_range
|
||||
try:
|
||||
errors = await _fetch_detailed_sheet_errors(
|
||||
service, spreadsheet_id, detailed_range
|
||||
@@ -233,7 +251,7 @@ async def read_sheet_values(
|
||||
)
|
||||
|
||||
logger.info(f"Successfully read {len(values)} rows for {user_google_email}.")
|
||||
return text_output + detailed_errors_section
|
||||
return text_output + hyperlink_section + detailed_errors_section
|
||||
|
||||
|
||||
@server.tool()
|
||||
|
||||
Reference in New Issue
Block a user