Skip to main content
Glama

Spire.XLS MCP Server

by eiceblue
cell_utils.py18.2 kB
import re from typing import Tuple, Optional, Any import datetime from spire.xls import * def letter_to_column(column_letter: str) -> int: """ Convert Excel column letter to column number. Example: A -> 1, Z -> 26, AA -> 27, etc. Args: column_letter: Excel column letter (A-Z, AA-ZZ, etc.) Returns: Column number (1-based index) """ if not column_letter or not column_letter.isalpha(): try: # If it's already a number, just return it return int(column_letter) except ValueError: raise ValueError(f"Invalid column letter: {column_letter}") column_letter = column_letter.upper() result = 0 for char in column_letter: result = result * 26 + (ord(char) - ord('A') + 1) return result def column_to_letter(column: int) -> str: """ Convert column number to Excel column letter. Example: 1 -> A, 26 -> Z, 27 -> AA, etc. Args: column: Column number (1-based index) Returns: Excel column letter (A-Z, AA-ZZ, etc.) """ if not isinstance(column, int) or column < 1: # If it's already a string, just return it if isinstance(column, str) and column.isalpha(): return column raise ValueError(f"Invalid column number: {column}") result = "" while column > 0: remainder = (column - 1) % 26 result = chr(ord('A') + remainder) + result column = (column - 1) // 26 return result def serialize_cell(cell, preview_only: bool): """Serialize a cell to a JSON-serializable dictionary object with null checks""" try: # Basic properties result = { "address": cell.RangeAddressLocal if hasattr(cell, "RangeAddressLocal") else None, "row": cell.Row if hasattr(cell, "Row") else None, "column": cell.Column if hasattr(cell, "Column") else None, "column_letter": column_to_letter(cell.Column) if hasattr(cell, "Column") else None, "value": None, "text": None, "formula": None, "has_formula": False } # Value handling (handle different types of values) try: result["value"] = cell.Value except: pass try: result["formula_value"] = cell.FormulaValue except: pass try: result["text"] = cell.Text except: pass # Formula handling try: result["has_formula"] = cell.HasFormula if cell.HasFormula: result["formula"] = cell.Formula except: pass if preview_only: return result # Style handling style_dict = {} # Font handling try: font_dict = {} if hasattr(cell, "Style") and hasattr(cell.Style, "Font"): font = cell.Style.Font if hasattr(font, "IsBold"): font_dict["bold"] = font.IsBold if hasattr(font, "IsItalic"): font_dict["italic"] = font.IsItalic if hasattr(font, "FontName"): font_dict["name"] = font.FontName if hasattr(font, "Size"): font_dict["size"] = font.Size if hasattr(font, "Color"): try: font_dict["color"] = { "r": font.Color.R, "g": font.Color.G, "b": font.Color.B } except: pass if hasattr(font, "Underline"): font_dict["underline"] = str(font.Underline) != "None" if font_dict: style_dict["font"] = font_dict except: pass # Alignment handling try: if hasattr(cell, "Style"): if hasattr(cell.Style, "HorizontalAlignment"): style_dict["horizontal_alignment"] = str(cell.Style.HorizontalAlignment) if hasattr(cell.Style, "VerticalAlignment"): style_dict["vertical_alignment"] = str(cell.Style.VerticalAlignment) if hasattr(cell.Style, "WrapText"): style_dict["wrap_text"] = cell.Style.WrapText if hasattr(cell.Style, "Rotation"): style_dict["rotation"] = cell.Style.Rotation if hasattr(cell.Style, "IndentLevel"): style_dict["indent_level"] = cell.Style.IndentLevel except: pass # Fill handling try: if hasattr(cell, "Style") and hasattr(cell.Style, "Interior"): interior_dict = {} interior = cell.Style.Interior if hasattr(interior, "Color"): try: interior_dict["color"] = { "r": interior.Color.R, "g": interior.Color.G, "b": interior.Color.B } except: pass if hasattr(interior, "FillPattern"): interior_dict["pattern"] = str(interior.FillPattern) if interior_dict: style_dict["fill"] = interior_dict except: pass # Borders handling try: if hasattr(cell, "Style") and hasattr(cell.Style, "Borders"): borders_dict = {} borders = cell.Style.Borders if hasattr(borders, "LineStyle"): borders_dict["line_style"] = str(borders.LineStyle) if hasattr(borders, "Color"): try: borders_dict["color"] = { "r": borders.Color.R, "g": borders.Color.G, "b": borders.Color.B } except: pass # Individual borders border_positions = ["Top", "Bottom", "Left", "Right"] for pos in border_positions: try: if hasattr(borders, pos): border = getattr(borders, pos) border_info = {} if hasattr(border, "LineStyle"): border_info["line_style"] = str(border.LineStyle) if hasattr(border, "Color"): try: border_info["color"] = { "r": border.Color.R, "g": border.Color.G, "b": border.Color.B } except: pass if border_info: borders_dict[pos.lower()] = border_info except: pass if borders_dict: style_dict["borders"] = borders_dict except: pass # Number format try: if hasattr(cell, "Style") and hasattr(cell.Style, "NumberFormat"): style_dict["number_format"] = cell.Style.NumberFormat except: pass # Cell protection try: if hasattr(cell, "Style"): protection_dict = {} if hasattr(cell.Style, "HideFormula"): protection_dict["hide_formula"] = cell.Style.HideFormula if protection_dict: style_dict["protection"] = protection_dict except: pass # Add style to result if style_dict: result["style"] = style_dict # Cell type try: if hasattr(cell, "Type"): result["cell_type"] = str(cell.Type) except: pass # Merged cells information try: if hasattr(cell, "IsMerged"): result["is_merged"] = cell.IsMerged if cell.IsMerged and hasattr(cell, "MergeArea"): try: area = cell.MergeArea result["merge_area"] = { "first_row": area.FirstRow, "first_column": area.FirstColumn, "last_row": area.LastRow, "last_column": area.LastColumn } except: pass except: pass return result except Exception as e: # Return basic information when any exception occurs return { "error": f"Failed to serialize cell: {str(e)}", "row": getattr(cell, "Row", None), "column": getattr(cell, "Column", None), "value": getattr(cell, "Value", None) } def parse_cell_range( start_cell: str, end_cell: Optional[str] = None, workbook: Workbook = Workbook() ) -> Tuple[int, int, Optional[int], Optional[int]]: """Parse Excel cell references into row and column numbers.""" try: start_cell = workbook.Worksheets[0].Range[start_cell] except: raise ValueError(f"Invalid start cell reference: {start_cell}") start_col = start_cell.Column start_row = start_cell.Row # Parse end cell if provided end_col = None end_row = None if end_cell: try: end_cell = workbook.Worksheets[0].Range[end_cell] except: raise ValueError(f"Invalid end cell reference: {end_cell}") end_col = end_cell.Column end_row = end_cell.Row return start_row, start_col, end_row, end_col def validate_cell_reference_regex(cell_ref: str) -> bool: """Validate Excel cell reference format.""" if not cell_ref: return False # Basic format validation pattern = r'^[A-Za-z]{1,3}[1-9][0-9]*$' if not re.match(pattern, cell_ref): return False return True class EnumMapper: SUBTOTAL_MAP = { "sum": SubtotalTypes.Sum, "average": SubtotalTypes.Average, "count": SubtotalTypes.Count, "min": SubtotalTypes.Min, "max": SubtotalTypes.Max } # Filter operator type mapping FILTER_OPERATOR_MAP = { "=": FilterOperatorType.Equal, ">": FilterOperatorType.GreaterThan, "<": FilterOperatorType.LessThan, ">=": FilterOperatorType.GreaterOrEqual, "<=": FilterOperatorType.LessOrEqual, "<>": FilterOperatorType.NotEqual } # condition format operator OPERATOR_MAP = { "greater": ComparisonOperatorType.Greater, "gt": ComparisonOperatorType.Greater, ">": ComparisonOperatorType.Greater, "大于": ComparisonOperatorType.Greater, "greater than": ComparisonOperatorType.Greater, "greater_or_equal": ComparisonOperatorType.GreaterOrEqual, ">=": ComparisonOperatorType.GreaterOrEqual, "ge": ComparisonOperatorType.GreaterOrEqual, "大于等于": ComparisonOperatorType.GreaterOrEqual, "less": ComparisonOperatorType.Less, "lt": ComparisonOperatorType.Less, "<": ComparisonOperatorType.Less, "小于": ComparisonOperatorType.Less, "less than": ComparisonOperatorType.Less, "less_or_equal": ComparisonOperatorType.LessOrEqual, "le": ComparisonOperatorType.LessOrEqual, "<=": ComparisonOperatorType.LessOrEqual, "小于等于": ComparisonOperatorType.LessOrEqual, "equal": ComparisonOperatorType.Equal, "eq": ComparisonOperatorType.Equal, "=": ComparisonOperatorType.Equal, "等于": ComparisonOperatorType.Equal, "not_equal": ComparisonOperatorType.NotEqual, "ne": ComparisonOperatorType.NotEqual, "!=": ComparisonOperatorType.NotEqual, "<>": ComparisonOperatorType.NotEqual, "不等于": ComparisonOperatorType.NotEqual, } # alignment ALIGNMENT_MAP = { "left": HorizontalAlignType.Left, "居左": HorizontalAlignType.Left, "center": HorizontalAlignType.Center, "居中": HorizontalAlignType.Center, "right": HorizontalAlignType.Right, "居右": HorizontalAlignType.Right, "justify": HorizontalAlignType.Justify, "两端对齐": HorizontalAlignType.Justify, } # border line style BORDER_STYLE_MAP = { "thin": LineStyleType.Thin, "细线": LineStyleType.Thin, "medium": LineStyleType.Medium, "中线": LineStyleType.Medium, "thick": LineStyleType.Thick, "粗线": LineStyleType.Thick, "double": LineStyleType.Double, "双线": LineStyleType.Double, } # chart type CHART_TYPE_MAP = { "column": ExcelChartType.ColumnClustered, "bar": ExcelChartType.BarClustered, "line": ExcelChartType.Line, "pie": ExcelChartType.Pie, "area": ExcelChartType.Area, "scatter": ExcelChartType.ScatterLine, "doughnut": ExcelChartType.Doughnut, "waterfall": ExcelChartType.WaterFall, "column_stacked": ExcelChartType.ColumnStacked, "column_100_percent_stacked": ExcelChartType.Column100PercentStacked, "bubble": ExcelChartType.Bubble, "funnel": ExcelChartType.Funnel, "treemap": ExcelChartType.TreeMap, "sunburst": ExcelChartType.SunBurst, "histogram": ExcelChartType.Histogram, "box_and_whisker": ExcelChartType.BoxAndWhisker, } # conditional format type CONDITION_TYPE_MAP = { "cell": ConditionalFormatType.CellValue, "单元格值": ConditionalFormatType.CellValue, "text": ConditionalFormatType.ContainsText, "文本": ConditionalFormatType.ContainsText, "date": ConditionalFormatType.TimePeriod, "日期": ConditionalFormatType.TimePeriod, "time_period": ConditionalFormatType.TimePeriod, "时间段": ConditionalFormatType.TimePeriod, "average": ConditionalFormatType.Average, "平均值": ConditionalFormatType.Average, "duplicate": ConditionalFormatType.DuplicateValues, "重复值": ConditionalFormatType.DuplicateValues, "unique": ConditionalFormatType.UniqueValues, "唯一值": ConditionalFormatType.UniqueValues, "formula": ConditionalFormatType.Formula, "公式": ConditionalFormatType.Formula, "top10": ConditionalFormatType.TopBottom, "前10项": ConditionalFormatType.TopBottom, "data_bar": ConditionalFormatType.DataBar, "数据条": ConditionalFormatType.DataBar, "color_scale": ConditionalFormatType.ColorScale, "色阶": ConditionalFormatType.ColorScale, "icon_set": ConditionalFormatType.IconSet, "图标集": ConditionalFormatType.IconSet } @staticmethod def smart_enum_map(input_str: str, mapping: dict, default): if not input_str: return default key = input_str.strip().lower() return mapping.get(key, default) @classmethod def get_operator_enum(cls, op_str: str) -> ComparisonOperatorType: return cls.smart_enum_map(op_str, cls.OPERATOR_MAP, ComparisonOperatorType.Greater) @classmethod def get_alignment_enum(cls, align_str: str) -> HorizontalAlignType: return cls.smart_enum_map(align_str, cls.ALIGNMENT_MAP, HorizontalAlignType.Left) @classmethod def get_border_style_enum(cls, style_str: str) -> LineStyleType: return cls.smart_enum_map(style_str, cls.BORDER_STYLE_MAP, LineStyleType.Thin) @classmethod def get_chart_type_enum(cls, chart_str: str) -> ExcelChartType: return cls.smart_enum_map(chart_str, cls.CHART_TYPE_MAP, ExcelChartType.ColumnClustered) @classmethod def get_condition_enum(cls, type_str: str) -> ConditionalFormatType: return cls.smart_enum_map(type_str, cls.CONDITION_TYPE_MAP, ConditionalFormatType.CellValue) @classmethod def get_subtotal_enum(cls, func_str: str) -> SubtotalTypes: return cls.smart_enum_map(func_str, cls.SUBTOTAL_MAP, SubtotalTypes.Sum) @classmethod def get_filter_operator_enum(cls, op_str: str) -> FilterOperatorType: return cls.smart_enum_map(op_str, cls.FILTER_OPERATOR_MAP, FilterOperatorType.Equal) def create_spire_object(value: Any) -> Any: """ Create corresponding SpireObject based on the input value's type. Args: value: The value to be converted to SpireObject Returns: SpireObject instance of corresponding type Examples: >>> create_spire_object(123) # Returns Int32(123) >>> create_spire_object(123.45) # Returns Double(123.45) >>> create_spire_object("text") # Returns String("text") >>> create_spire_object(True) # Returns Boolean(True) >>> create_spire_object(datetime.datetime(2023, 1, 1)) # Returns DateTime(2023, 1, 1) """ if value is None: return String("") if isinstance(value, bool): return Boolean(value) if isinstance(value, int): if -2147483648 <= value <= 2147483647: return Int32(value) else: return Int64(value) if isinstance(value, float): return Double(value) if isinstance(value, str): return String(value) if isinstance(value, (datetime.datetime, datetime.date)): return DateTime(value.year, value.month, value.day, getattr(value, 'hour', 0), getattr(value, 'minute', 0), getattr(value, 'second', 0), getattr(value, 'microsecond', 0)) return String(str(value))

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eiceblue/spire-xls-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server