Add hyperlink support to modify_doc_text

This commit is contained in:
Gigi Sayfan
2026-02-07 13:44:09 -08:00
parent 79a578eda9
commit 34957de2e4
6 changed files with 54 additions and 6 deletions

View File

@@ -46,6 +46,7 @@ def build_text_style(
font_family: str = None,
text_color: str = None,
background_color: str = None,
link_url: str = None,
) -> tuple[Dict[str, Any], list[str]]:
"""
Build text style object for Google Docs API requests.
@@ -58,6 +59,7 @@ def build_text_style(
font_family: Font family name
text_color: Text color as hex string "#RRGGBB"
background_color: Background (highlight) color as hex string "#RRGGBB"
link_url: Hyperlink URL (http/https)
Returns:
Tuple of (text_style_dict, list_of_field_names)
@@ -95,6 +97,10 @@ def build_text_style(
text_style["backgroundColor"] = {"color": {"rgbColor": rgb}}
fields.append("backgroundColor")
if link_url is not None:
text_style["link"] = {"url": link_url}
fields.append("link")
return text_style, fields
@@ -162,6 +168,7 @@ def create_format_text_request(
font_family: str = None,
text_color: str = None,
background_color: str = None,
link_url: str = None,
) -> Optional[Dict[str, Any]]:
"""
Create an updateTextStyle request for Google Docs API.
@@ -176,12 +183,20 @@ def create_format_text_request(
font_family: Font family name
text_color: Text color as hex string "#RRGGBB"
background_color: Background (highlight) color as hex string "#RRGGBB"
link_url: Hyperlink URL (http/https)
Returns:
Dictionary representing the updateTextStyle request, or None if no styles provided
"""
text_style, fields = build_text_style(
bold, italic, underline, font_size, font_family, text_color, background_color
bold,
italic,
underline,
font_size,
font_family,
text_color,
background_color,
link_url,
)
if not text_style:

View File

@@ -367,6 +367,7 @@ async def modify_doc_text(
font_family: str = None,
text_color: str = None,
background_color: str = None,
link_url: str = None,
) -> str:
"""
Modifies text in a Google Doc - can insert/replace text and/or apply formatting in a single operation.
@@ -384,13 +385,14 @@ async def modify_doc_text(
font_family: Font family name (e.g., "Arial", "Times New Roman")
text_color: Foreground text color (#RRGGBB)
background_color: Background/highlight color (#RRGGBB)
link_url: Hyperlink URL (http/https)
Returns:
str: Confirmation message with operation details
"""
logger.info(
f"[modify_doc_text] Doc={document_id}, start={start_index}, end={end_index}, text={text is not None}, "
f"formatting={any([bold, italic, underline, font_size, font_family, text_color, background_color])}"
f"formatting={any([bold, italic, underline, font_size, font_family, text_color, background_color, link_url])}"
)
# Input validation
@@ -410,9 +412,10 @@ async def modify_doc_text(
font_family,
text_color,
background_color,
link_url,
]
):
return "Error: Must provide either 'text' to insert/replace, or formatting parameters (bold, italic, underline, font_size, font_family, text_color, background_color)."
return "Error: Must provide either 'text' to insert/replace, or formatting parameters (bold, italic, underline, font_size, font_family, text_color, background_color, link_url)."
# Validate text formatting params if provided
if any(
@@ -434,6 +437,7 @@ async def modify_doc_text(
font_family,
text_color,
background_color,
link_url,
)
if not is_valid:
return f"Error: {error_msg}"
@@ -491,6 +495,7 @@ async def modify_doc_text(
font_family,
text_color,
background_color,
link_url,
]
):
# Adjust range for formatting based on text operations
@@ -524,6 +529,7 @@ async def modify_doc_text(
font_family,
text_color,
background_color,
link_url,
)
)
@@ -542,6 +548,8 @@ async def modify_doc_text(
format_details.append(f"text_color={text_color}")
if background_color:
format_details.append(f"background_color={background_color}")
if link_url:
format_details.append(f"link_url={link_url}")
operations.append(
f"Applied formatting ({', '.join(format_details)}) to range {format_start}-{format_end}"

View File

@@ -189,6 +189,7 @@ class BatchOperationManager:
op.get("font_family"),
op.get("text_color"),
op.get("background_color"),
op.get("link_url"),
)
if not request:
@@ -204,6 +205,7 @@ class BatchOperationManager:
("font_family", "font family"),
("text_color", "text color"),
("background_color", "background color"),
("link_url", "link"),
]:
if op.get(param) is not None:
value = f"{op[param]}pt" if param == "font_size" else op[param]

View File

@@ -157,6 +157,7 @@ class ValidationManager:
font_family: Optional[str] = None,
text_color: Optional[str] = None,
background_color: Optional[str] = None,
link_url: Optional[str] = None,
) -> Tuple[bool, str]:
"""
Validate text formatting parameters.
@@ -169,6 +170,7 @@ class ValidationManager:
font_family: Font family name
text_color: Text color in "#RRGGBB" format
background_color: Background color in "#RRGGBB" format
link_url: Hyperlink URL (http/https)
Returns:
Tuple of (is_valid, error_message)
@@ -182,11 +184,12 @@ class ValidationManager:
font_family,
text_color,
background_color,
link_url,
]
if all(param is None for param in formatting_params):
return (
False,
"At least one formatting parameter must be provided (bold, italic, underline, font_size, font_family, text_color, or background_color)",
"At least one formatting parameter must be provided (bold, italic, underline, font_size, font_family, text_color, background_color, or link_url)",
)
# Validate boolean parameters
@@ -238,8 +241,27 @@ class ValidationManager:
if not is_valid:
return False, error_msg
is_valid, error_msg = self.validate_link_url(link_url)
if not is_valid:
return False, error_msg
return True, ""
def validate_link_url(self, link_url: Optional[str]) -> Tuple[bool, str]:
"""Validate hyperlink URL parameters."""
if link_url is None:
return True, ""
if not isinstance(link_url, str):
return False, f"link_url must be a string, got {type(link_url).__name__}"
if not link_url.strip():
return False, "link_url cannot be empty"
if not (link_url.startswith("http://") or link_url.startswith("https://")):
return False, "link_url must start with http:// or https://"
return True, ""
def validate_color_param(
self, color: Optional[str], param_name: str
) -> Tuple[bool, str]:
@@ -479,6 +501,7 @@ class ValidationManager:
op.get("font_family"),
op.get("text_color"),
op.get("background_color"),
op.get("link_url"),
)
if not is_valid:
return False, f"Operation {i + 1} (format_text): {error_msg}"