apply ruff formatting
This commit is contained in:
@@ -15,9 +15,9 @@ from .sheets_tools import (
|
||||
|
||||
__all__ = [
|
||||
"list_spreadsheets",
|
||||
"get_spreadsheet_info",
|
||||
"get_spreadsheet_info",
|
||||
"read_sheet_values",
|
||||
"modify_sheet_values",
|
||||
"create_spreadsheet",
|
||||
"create_sheet",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -85,8 +85,7 @@ def _parse_a1_range(range_name: str, sheets: List[dict]) -> dict:
|
||||
break
|
||||
if target_sheet is None:
|
||||
available_titles = [
|
||||
sheet.get("properties", {}).get("title", "Untitled")
|
||||
for sheet in sheets
|
||||
sheet.get("properties", {}).get("title", "Untitled") for sheet in sheets
|
||||
]
|
||||
available_list = ", ".join(available_titles) if available_titles else "none"
|
||||
raise UserInputError(
|
||||
@@ -209,7 +208,9 @@ def _grid_range_to_a1(grid_range: dict, sheet_titles: dict[int, str]) -> str:
|
||||
end_label = f"{col_label(end_col - 1 if end_col is not None else None)}{row_label(end_row - 1 if end_row is not None else None)}"
|
||||
|
||||
if start_label and end_label:
|
||||
range_ref = start_label if start_label == end_label else f"{start_label}:{end_label}"
|
||||
range_ref = (
|
||||
start_label if start_label == end_label else f"{start_label}:{end_label}"
|
||||
)
|
||||
elif start_label:
|
||||
range_ref = start_label
|
||||
elif end_label:
|
||||
@@ -227,7 +228,9 @@ def _summarize_conditional_rule(
|
||||
Produce a concise human-readable summary of a conditional formatting rule.
|
||||
"""
|
||||
ranges = rule.get("ranges", [])
|
||||
range_labels = [_grid_range_to_a1(rng, sheet_titles) for rng in ranges] or ["(no range)"]
|
||||
range_labels = [_grid_range_to_a1(rng, sheet_titles) for rng in ranges] or [
|
||||
"(no range)"
|
||||
]
|
||||
|
||||
if "booleanRule" in rule:
|
||||
boolean_rule = rule["booleanRule"]
|
||||
@@ -288,7 +291,9 @@ def _format_conditional_rules_section(
|
||||
|
||||
lines = [f'{indent}Conditional formats for "{sheet_title}" ({len(rules)}):']
|
||||
for idx, rule in enumerate(rules):
|
||||
lines.append(f"{indent} {_summarize_conditional_rule(rule, idx, sheet_titles)}")
|
||||
lines.append(
|
||||
f"{indent} {_summarize_conditional_rule(rule, idx, sheet_titles)}"
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@@ -379,7 +384,7 @@ def _parse_condition_values(
|
||||
parsed = json.loads(parsed)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise UserInputError(
|
||||
'condition_values must be a list or a JSON-encoded list (e.g., \'["=$B2>1000"]\').'
|
||||
"condition_values must be a list or a JSON-encoded list (e.g., '[\"=$B2>1000\"]')."
|
||||
) from exc
|
||||
|
||||
if parsed is not None and not isinstance(parsed, list):
|
||||
@@ -396,7 +401,7 @@ def _parse_condition_values(
|
||||
|
||||
|
||||
def _parse_gradient_points(
|
||||
gradient_points: Optional[Union[str, List[dict]]]
|
||||
gradient_points: Optional[Union[str, List[dict]]],
|
||||
) -> Optional[List[dict]]:
|
||||
"""
|
||||
Normalize gradient points into a list of dicts with type/value/color.
|
||||
@@ -424,7 +429,9 @@ def _parse_gradient_points(
|
||||
normalized_points: List[dict] = []
|
||||
for idx, point in enumerate(parsed):
|
||||
if not isinstance(point, dict):
|
||||
raise UserInputError(f"gradient_points[{idx}] must be an object with type/color.")
|
||||
raise UserInputError(
|
||||
f"gradient_points[{idx}] must be an object with type/color."
|
||||
)
|
||||
|
||||
point_type = point.get("type")
|
||||
if not point_type or point_type.upper() not in GRADIENT_POINT_TYPES:
|
||||
@@ -432,7 +439,11 @@ def _parse_gradient_points(
|
||||
f"gradient_points[{idx}].type must be one of {sorted(GRADIENT_POINT_TYPES)}."
|
||||
)
|
||||
color_raw = point.get("color")
|
||||
color_dict = _parse_hex_color(color_raw) if not isinstance(color_raw, dict) else color_raw
|
||||
color_dict = (
|
||||
_parse_hex_color(color_raw)
|
||||
if not isinstance(color_raw, dict)
|
||||
else color_raw
|
||||
)
|
||||
if not color_dict:
|
||||
raise UserInputError(f"gradient_points[{idx}].color is required.")
|
||||
|
||||
@@ -462,7 +473,9 @@ def _build_boolean_rule(
|
||||
|
||||
cond_type_normalized = condition_type.upper()
|
||||
if cond_type_normalized not in CONDITION_TYPES:
|
||||
raise UserInputError(f"condition_type must be one of {sorted(CONDITION_TYPES)}.")
|
||||
raise UserInputError(
|
||||
f"condition_type must be one of {sorted(CONDITION_TYPES)}."
|
||||
)
|
||||
|
||||
condition = {"type": cond_type_normalized}
|
||||
if condition_values:
|
||||
|
||||
@@ -30,6 +30,7 @@ from gsheets.sheets_helpers import (
|
||||
# Configure module logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@server.tool()
|
||||
@handle_http_errors("list_spreadsheets", is_read_only=True, service_type="sheets")
|
||||
@require_google_service("drive", "drive_read")
|
||||
@@ -68,7 +69,7 @@ async def list_spreadsheets(
|
||||
return f"No spreadsheets found for {user_google_email}."
|
||||
|
||||
spreadsheets_list = [
|
||||
f"- \"{file['name']}\" (ID: {file['id']}) | Modified: {file.get('modifiedTime', 'Unknown')} | Link: {file.get('webViewLink', 'No link')}"
|
||||
f'- "{file["name"]}" (ID: {file["id"]}) | Modified: {file.get("modifiedTime", "Unknown")} | Link: {file.get("webViewLink", "No link")}'
|
||||
for file in files
|
||||
]
|
||||
|
||||
@@ -77,7 +78,9 @@ async def list_spreadsheets(
|
||||
+ "\n".join(spreadsheets_list)
|
||||
)
|
||||
|
||||
logger.info(f"Successfully listed {len(files)} spreadsheets for {user_google_email}.")
|
||||
logger.info(
|
||||
f"Successfully listed {len(files)} spreadsheets for {user_google_email}."
|
||||
)
|
||||
return text_output
|
||||
|
||||
|
||||
@@ -99,7 +102,9 @@ async def get_spreadsheet_info(
|
||||
Returns:
|
||||
str: Formatted spreadsheet information including title, locale, and sheets list.
|
||||
"""
|
||||
logger.info(f"[get_spreadsheet_info] Invoked. Email: '{user_google_email}', Spreadsheet ID: {spreadsheet_id}")
|
||||
logger.info(
|
||||
f"[get_spreadsheet_info] Invoked. Email: '{user_google_email}', Spreadsheet ID: {spreadsheet_id}"
|
||||
)
|
||||
|
||||
spreadsheet = await asyncio.to_thread(
|
||||
service.spreadsheets()
|
||||
@@ -133,7 +138,7 @@ async def get_spreadsheet_info(
|
||||
rules = sheet.get("conditionalFormats", []) or []
|
||||
|
||||
sheets_info.append(
|
||||
f" - \"{sheet_name}\" (ID: {sheet_id}) | Size: {rows}x{cols} | Conditional formats: {len(rules)}"
|
||||
f' - "{sheet_name}" (ID: {sheet_id}) | Size: {rows}x{cols} | Conditional formats: {len(rules)}'
|
||||
)
|
||||
if rules:
|
||||
sheets_info.append(
|
||||
@@ -145,13 +150,15 @@ async def get_spreadsheet_info(
|
||||
sheets_section = "\n".join(sheets_info) if sheets_info else " No sheets found"
|
||||
text_output = "\n".join(
|
||||
[
|
||||
f"Spreadsheet: \"{title}\" (ID: {spreadsheet_id}) | Locale: {locale}",
|
||||
f'Spreadsheet: "{title}" (ID: {spreadsheet_id}) | Locale: {locale}',
|
||||
f"Sheets ({len(sheets)}):",
|
||||
sheets_section,
|
||||
]
|
||||
)
|
||||
|
||||
logger.info(f"Successfully retrieved info for spreadsheet {spreadsheet_id} for {user_google_email}.")
|
||||
logger.info(
|
||||
f"Successfully retrieved info for spreadsheet {spreadsheet_id} for {user_google_email}."
|
||||
)
|
||||
return text_output
|
||||
|
||||
|
||||
@@ -175,7 +182,9 @@ async def read_sheet_values(
|
||||
Returns:
|
||||
str: The formatted values from the specified range.
|
||||
"""
|
||||
logger.info(f"[read_sheet_values] Invoked. Email: '{user_google_email}', Spreadsheet: {spreadsheet_id}, Range: {range_name}")
|
||||
logger.info(
|
||||
f"[read_sheet_values] Invoked. Email: '{user_google_email}', Spreadsheet: {spreadsheet_id}, Range: {range_name}"
|
||||
)
|
||||
|
||||
result = await asyncio.to_thread(
|
||||
service.spreadsheets()
|
||||
@@ -232,27 +241,37 @@ async def modify_sheet_values(
|
||||
str: Confirmation message of the successful modification operation.
|
||||
"""
|
||||
operation = "clear" if clear_values else "write"
|
||||
logger.info(f"[modify_sheet_values] Invoked. Operation: {operation}, Email: '{user_google_email}', Spreadsheet: {spreadsheet_id}, Range: {range_name}")
|
||||
logger.info(
|
||||
f"[modify_sheet_values] Invoked. Operation: {operation}, Email: '{user_google_email}', Spreadsheet: {spreadsheet_id}, Range: {range_name}"
|
||||
)
|
||||
|
||||
# Parse values if it's a JSON string (MCP passes parameters as JSON strings)
|
||||
if values is not None and isinstance(values, str):
|
||||
try:
|
||||
parsed_values = json.loads(values)
|
||||
if not isinstance(parsed_values, list):
|
||||
raise ValueError(f"Values must be a list, got {type(parsed_values).__name__}")
|
||||
raise ValueError(
|
||||
f"Values must be a list, got {type(parsed_values).__name__}"
|
||||
)
|
||||
# Validate it's a list of lists
|
||||
for i, row in enumerate(parsed_values):
|
||||
if not isinstance(row, list):
|
||||
raise ValueError(f"Row {i} must be a list, got {type(row).__name__}")
|
||||
raise ValueError(
|
||||
f"Row {i} must be a list, got {type(row).__name__}"
|
||||
)
|
||||
values = parsed_values
|
||||
logger.info(f"[modify_sheet_values] Parsed JSON string to Python list with {len(values)} rows")
|
||||
logger.info(
|
||||
f"[modify_sheet_values] Parsed JSON string to Python list with {len(values)} rows"
|
||||
)
|
||||
except json.JSONDecodeError as e:
|
||||
raise UserInputError(f"Invalid JSON format for values: {e}")
|
||||
except ValueError as e:
|
||||
raise UserInputError(f"Invalid values structure: {e}")
|
||||
|
||||
if not clear_values and not values:
|
||||
raise UserInputError("Either 'values' must be provided or 'clear_values' must be True.")
|
||||
raise UserInputError(
|
||||
"Either 'values' must be provided or 'clear_values' must be True."
|
||||
)
|
||||
|
||||
if clear_values:
|
||||
result = await asyncio.to_thread(
|
||||
@@ -264,7 +283,9 @@ async def modify_sheet_values(
|
||||
|
||||
cleared_range = result.get("clearedRange", range_name)
|
||||
text_output = f"Successfully cleared range '{cleared_range}' in spreadsheet {spreadsheet_id} for {user_google_email}."
|
||||
logger.info(f"Successfully cleared range '{cleared_range}' for {user_google_email}.")
|
||||
logger.info(
|
||||
f"Successfully cleared range '{cleared_range}' for {user_google_email}."
|
||||
)
|
||||
else:
|
||||
body = {"values": values}
|
||||
|
||||
@@ -288,7 +309,9 @@ async def modify_sheet_values(
|
||||
f"Successfully updated range '{range_name}' in spreadsheet {spreadsheet_id} for {user_google_email}. "
|
||||
f"Updated: {updated_cells} cells, {updated_rows} rows, {updated_columns} columns."
|
||||
)
|
||||
logger.info(f"Successfully updated {updated_cells} cells for {user_google_email}.")
|
||||
logger.info(
|
||||
f"Successfully updated {updated_cells} cells for {user_google_email}."
|
||||
)
|
||||
|
||||
return text_output
|
||||
|
||||
@@ -380,9 +403,7 @@ async def format_sheet_range(
|
||||
user_entered_format["backgroundColor"] = bg_color_parsed
|
||||
fields.append("userEnteredFormat.backgroundColor")
|
||||
if text_color_parsed:
|
||||
user_entered_format["textFormat"] = {
|
||||
"foregroundColor": text_color_parsed
|
||||
}
|
||||
user_entered_format["textFormat"] = {"foregroundColor": text_color_parsed}
|
||||
fields.append("userEnteredFormat.textFormat.foregroundColor")
|
||||
if number_format:
|
||||
user_entered_format["numberFormat"] = number_format
|
||||
@@ -485,7 +506,9 @@ async def add_conditional_formatting(
|
||||
target_sheet = sheet
|
||||
break
|
||||
if target_sheet is None:
|
||||
raise UserInputError("Target sheet not found while adding conditional formatting.")
|
||||
raise UserInputError(
|
||||
"Target sheet not found while adding conditional formatting."
|
||||
)
|
||||
|
||||
current_rules = target_sheet.get("conditionalFormats", []) or []
|
||||
|
||||
@@ -613,7 +636,9 @@ async def update_conditional_formatting(
|
||||
target_sheet = _select_sheet(sheets, sheet_name)
|
||||
|
||||
if target_sheet is None:
|
||||
raise UserInputError("Target sheet not found while updating conditional formatting.")
|
||||
raise UserInputError(
|
||||
"Target sheet not found while updating conditional formatting."
|
||||
)
|
||||
|
||||
sheet_props = target_sheet.get("properties", {})
|
||||
sheet_id = sheet_props.get("sheetId")
|
||||
@@ -661,10 +686,14 @@ async def update_conditional_formatting(
|
||||
if not cond_type:
|
||||
raise UserInputError("condition_type is required for boolean rules.")
|
||||
if cond_type not in CONDITION_TYPES:
|
||||
raise UserInputError(f"condition_type must be one of {sorted(CONDITION_TYPES)}.")
|
||||
raise UserInputError(
|
||||
f"condition_type must be one of {sorted(CONDITION_TYPES)}."
|
||||
)
|
||||
|
||||
if condition_values_list is not None:
|
||||
cond_values = [{"userEnteredValue": str(val)} for val in condition_values_list]
|
||||
cond_values = [
|
||||
{"userEnteredValue": str(val)} for val in condition_values_list
|
||||
]
|
||||
else:
|
||||
cond_values = existing_condition.get("values")
|
||||
|
||||
@@ -706,7 +735,9 @@ async def update_conditional_formatting(
|
||||
format_parts = []
|
||||
if "backgroundColor" in new_format:
|
||||
format_parts.append("background updated")
|
||||
if "textFormat" in new_format and new_format["textFormat"].get("foregroundColor"):
|
||||
if "textFormat" in new_format and new_format["textFormat"].get(
|
||||
"foregroundColor"
|
||||
):
|
||||
format_parts.append("text color updated")
|
||||
format_desc = ", ".join(format_parts) if format_parts else "format preserved"
|
||||
|
||||
@@ -841,13 +872,11 @@ async def create_spreadsheet(
|
||||
Returns:
|
||||
str: Information about the newly created spreadsheet including ID, URL, and locale.
|
||||
"""
|
||||
logger.info(f"[create_spreadsheet] Invoked. Email: '{user_google_email}', Title: {title}")
|
||||
logger.info(
|
||||
f"[create_spreadsheet] Invoked. Email: '{user_google_email}', Title: {title}"
|
||||
)
|
||||
|
||||
spreadsheet_body = {
|
||||
"properties": {
|
||||
"title": title
|
||||
}
|
||||
}
|
||||
spreadsheet_body = {"properties": {"title": title}}
|
||||
|
||||
if sheet_names:
|
||||
spreadsheet_body["sheets"] = [
|
||||
@@ -873,7 +902,9 @@ async def create_spreadsheet(
|
||||
f"ID: {spreadsheet_id} | URL: {spreadsheet_url} | Locale: {locale}"
|
||||
)
|
||||
|
||||
logger.info(f"Successfully created spreadsheet for {user_google_email}. ID: {spreadsheet_id}")
|
||||
logger.info(
|
||||
f"Successfully created spreadsheet for {user_google_email}. ID: {spreadsheet_id}"
|
||||
)
|
||||
return text_output
|
||||
|
||||
|
||||
@@ -897,19 +928,11 @@ async def create_sheet(
|
||||
Returns:
|
||||
str: Confirmation message of the successful sheet creation.
|
||||
"""
|
||||
logger.info(f"[create_sheet] Invoked. Email: '{user_google_email}', Spreadsheet: {spreadsheet_id}, Sheet: {sheet_name}")
|
||||
logger.info(
|
||||
f"[create_sheet] Invoked. Email: '{user_google_email}', Spreadsheet: {spreadsheet_id}, Sheet: {sheet_name}"
|
||||
)
|
||||
|
||||
request_body = {
|
||||
"requests": [
|
||||
{
|
||||
"addSheet": {
|
||||
"properties": {
|
||||
"title": sheet_name
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
request_body = {"requests": [{"addSheet": {"properties": {"title": sheet_name}}}]}
|
||||
|
||||
response = await asyncio.to_thread(
|
||||
service.spreadsheets()
|
||||
@@ -919,11 +942,11 @@ async def create_sheet(
|
||||
|
||||
sheet_id = response["replies"][0]["addSheet"]["properties"]["sheetId"]
|
||||
|
||||
text_output = (
|
||||
f"Successfully created sheet '{sheet_name}' (ID: {sheet_id}) in spreadsheet {spreadsheet_id} for {user_google_email}."
|
||||
)
|
||||
text_output = f"Successfully created sheet '{sheet_name}' (ID: {sheet_id}) in spreadsheet {spreadsheet_id} for {user_google_email}."
|
||||
|
||||
logger.info(f"Successfully created sheet for {user_google_email}. Sheet ID: {sheet_id}")
|
||||
logger.info(
|
||||
f"Successfully created sheet for {user_google_email}. Sheet ID: {sheet_id}"
|
||||
)
|
||||
return text_output
|
||||
|
||||
|
||||
@@ -931,7 +954,7 @@ async def create_sheet(
|
||||
_comment_tools = create_comment_tools("spreadsheet", "spreadsheet_id")
|
||||
|
||||
# Extract and register the functions
|
||||
read_sheet_comments = _comment_tools['read_comments']
|
||||
create_sheet_comment = _comment_tools['create_comment']
|
||||
reply_to_sheet_comment = _comment_tools['reply_to_comment']
|
||||
resolve_sheet_comment = _comment_tools['resolve_comment']
|
||||
read_sheet_comments = _comment_tools["read_comments"]
|
||||
create_sheet_comment = _comment_tools["create_comment"]
|
||||
reply_to_sheet_comment = _comment_tools["reply_to_comment"]
|
||||
resolve_sheet_comment = _comment_tools["resolve_comment"]
|
||||
|
||||
Reference in New Issue
Block a user