Spaces:
Running
Running
| # MCP Data Transformer Module | |
| """ | |
| Advanced Data Transformation Tools for MCP Integration. | |
| Features: | |
| - Format conversion (CSV ↔ JSON ↔ Excel) | |
| - Pivot/unpivot operations | |
| - Column mapping and renaming | |
| - Data aggregation | |
| - Schema inference | |
| - Data type conversion | |
| """ | |
| import json | |
| import io | |
| from dataclasses import dataclass | |
| from typing import List, Dict, Optional, Any, Union | |
| from datetime import datetime | |
| class TransformResult: | |
| """Result from data transformation""" | |
| success: bool | |
| data: Any | |
| message: str | |
| rows_before: int = 0 | |
| rows_after: int = 0 | |
| columns: List[str] = None | |
| metadata: Dict = None | |
| class DataTransformer: | |
| """ | |
| Enterprise Data Transformer MCP. | |
| Provides powerful data transformation capabilities: | |
| - Format conversions | |
| - Pivot operations | |
| - Aggregations | |
| - Schema transformations | |
| """ | |
| def __init__(self): | |
| self.supported_formats = ['csv', 'json', 'excel', 'dict'] | |
| def convert_format( | |
| self, | |
| data: Any, | |
| source_format: str, | |
| target_format: str, | |
| **options | |
| ) -> Dict[str, Any]: | |
| """ | |
| Convert data between formats. | |
| Args: | |
| data: Input data | |
| source_format: 'csv', 'json', 'excel', 'dict' | |
| target_format: Desired output format | |
| options: Format-specific options | |
| Returns: | |
| Converted data with metadata | |
| """ | |
| try: | |
| import pandas as pd | |
| # Parse input | |
| df = self._parse_input(data, source_format) | |
| if df is None: | |
| return {"success": False, "error": "Could not parse input data"} | |
| # Convert to output format | |
| output = self._to_format(df, target_format, **options) | |
| return { | |
| "success": True, | |
| "data": output, | |
| "source_format": source_format, | |
| "target_format": target_format, | |
| "rows": len(df), | |
| "columns": list(df.columns) | |
| } | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |
| def _parse_input(self, data: Any, format_type: str) -> Any: | |
| """Parse input data based on format type""" | |
| import pandas as pd | |
| if isinstance(data, pd.DataFrame): | |
| return data | |
| if format_type == 'csv': | |
| if isinstance(data, str): | |
| return pd.read_csv(io.StringIO(data)) | |
| elif format_type == 'json': | |
| if isinstance(data, str): | |
| parsed = json.loads(data) | |
| if isinstance(parsed, list): | |
| return pd.DataFrame(parsed) | |
| return pd.DataFrame([parsed]) | |
| elif isinstance(data, (list, dict)): | |
| if isinstance(data, list): | |
| return pd.DataFrame(data) | |
| return pd.DataFrame([data]) | |
| elif format_type == 'dict': | |
| if isinstance(data, dict): | |
| if 'data' in data: | |
| return pd.DataFrame(data['data']) | |
| return pd.DataFrame(data) | |
| elif isinstance(data, list): | |
| return pd.DataFrame(data) | |
| # Try pandas auto-detection | |
| try: | |
| return pd.DataFrame(data) | |
| except: | |
| return None | |
| def _to_format(self, df: Any, format_type: str, **options) -> Any: | |
| """Convert DataFrame to specified format""" | |
| if format_type == 'csv': | |
| return df.to_csv(index=False) | |
| elif format_type == 'json': | |
| orient = options.get('orient', 'records') | |
| return df.to_json(orient=orient) | |
| elif format_type == 'dict': | |
| return df.to_dict(orient='records') | |
| elif format_type == 'excel': | |
| output = io.BytesIO() | |
| df.to_excel(output, index=False) | |
| return output.getvalue() | |
| return df.to_dict(orient='records') | |
| def pivot_data( | |
| self, | |
| data: Any, | |
| index: str, | |
| columns: str, | |
| values: str, | |
| aggfunc: str = 'sum' | |
| ) -> Dict[str, Any]: | |
| """ | |
| Pivot data to restructure it. | |
| Args: | |
| data: Input data | |
| index: Column to use as row index | |
| columns: Column to pivot on | |
| values: Column with values | |
| aggfunc: Aggregation function ('sum', 'mean', 'count', 'max', 'min') | |
| Returns: | |
| Pivoted data | |
| """ | |
| try: | |
| import pandas as pd | |
| if isinstance(data, pd.DataFrame): | |
| df = data.copy() | |
| else: | |
| df = pd.DataFrame(data) | |
| pivot_df = df.pivot_table( | |
| index=index, | |
| columns=columns, | |
| values=values, | |
| aggfunc=aggfunc | |
| ).reset_index() | |
| # Flatten column names if MultiIndex | |
| pivot_df.columns = [ | |
| f"{a}_{b}" if b else a | |
| for a, b in pivot_df.columns | |
| ] if pivot_df.columns.nlevels > 1 else pivot_df.columns | |
| return { | |
| "success": True, | |
| "data": pivot_df.to_dict(orient='records'), | |
| "rows": len(pivot_df), | |
| "columns": list(pivot_df.columns), | |
| "operation": "pivot" | |
| } | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |
| def unpivot_data( | |
| self, | |
| data: Any, | |
| id_vars: List[str], | |
| value_vars: Optional[List[str]] = None, | |
| var_name: str = 'variable', | |
| value_name: str = 'value' | |
| ) -> Dict[str, Any]: | |
| """ | |
| Unpivot (melt) data from wide to long format. | |
| Args: | |
| data: Input data | |
| id_vars: Columns to keep as identifiers | |
| value_vars: Columns to unpivot (None = all others) | |
| var_name: Name for variable column | |
| value_name: Name for value column | |
| Returns: | |
| Unpivoted data | |
| """ | |
| try: | |
| import pandas as pd | |
| if isinstance(data, pd.DataFrame): | |
| df = data.copy() | |
| else: | |
| df = pd.DataFrame(data) | |
| melted = pd.melt( | |
| df, | |
| id_vars=id_vars, | |
| value_vars=value_vars, | |
| var_name=var_name, | |
| value_name=value_name | |
| ) | |
| return { | |
| "success": True, | |
| "data": melted.to_dict(orient='records'), | |
| "rows": len(melted), | |
| "columns": list(melted.columns), | |
| "operation": "unpivot" | |
| } | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |
| def aggregate_data( | |
| self, | |
| data: Any, | |
| group_by: List[str], | |
| aggregations: Dict[str, str] | |
| ) -> Dict[str, Any]: | |
| """ | |
| Aggregate data with grouping. | |
| Args: | |
| data: Input data | |
| group_by: Columns to group by | |
| aggregations: {column: aggfunc} mapping | |
| e.g. {'revenue': 'sum', 'orders': 'count'} | |
| Returns: | |
| Aggregated data | |
| """ | |
| try: | |
| import pandas as pd | |
| if isinstance(data, pd.DataFrame): | |
| df = data.copy() | |
| else: | |
| df = pd.DataFrame(data) | |
| # Build aggregation dict | |
| agg_dict = {} | |
| for col, func in aggregations.items(): | |
| if col in df.columns: | |
| agg_dict[col] = func | |
| if not agg_dict: | |
| return {"success": False, "error": "No valid aggregation columns"} | |
| result = df.groupby(group_by).agg(agg_dict).reset_index() | |
| return { | |
| "success": True, | |
| "data": result.to_dict(orient='records'), | |
| "rows": len(result), | |
| "columns": list(result.columns), | |
| "groups": len(result), | |
| "operation": "aggregate" | |
| } | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |
| def rename_columns( | |
| self, | |
| data: Any, | |
| mapping: Dict[str, str] | |
| ) -> Dict[str, Any]: | |
| """ | |
| Rename columns in data. | |
| Args: | |
| data: Input data | |
| mapping: {old_name: new_name} mapping | |
| Returns: | |
| Data with renamed columns | |
| """ | |
| try: | |
| import pandas as pd | |
| if isinstance(data, pd.DataFrame): | |
| df = data.copy() | |
| else: | |
| df = pd.DataFrame(data) | |
| df = df.rename(columns=mapping) | |
| return { | |
| "success": True, | |
| "data": df.to_dict(orient='records'), | |
| "renamed": list(mapping.keys()), | |
| "new_names": list(mapping.values()), | |
| "columns": list(df.columns) | |
| } | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |
| def infer_schema(self, data: Any) -> Dict[str, Any]: | |
| """ | |
| Infer data schema with types and statistics. | |
| Args: | |
| data: Input data | |
| Returns: | |
| Schema information | |
| """ | |
| try: | |
| import pandas as pd | |
| import numpy as np | |
| if isinstance(data, pd.DataFrame): | |
| df = data.copy() | |
| else: | |
| df = pd.DataFrame(data) | |
| schema = { | |
| "columns": [], | |
| "row_count": len(df), | |
| "column_count": len(df.columns) | |
| } | |
| for col in df.columns: | |
| col_info = { | |
| "name": col, | |
| "dtype": str(df[col].dtype), | |
| "null_count": int(df[col].isna().sum()), | |
| "unique_count": int(df[col].nunique()), | |
| "sample_values": df[col].dropna().head(3).tolist() | |
| } | |
| # Add statistics for numeric columns | |
| if df[col].dtype in ['int64', 'float64']: | |
| col_info.update({ | |
| "min": float(df[col].min()) if not pd.isna(df[col].min()) else None, | |
| "max": float(df[col].max()) if not pd.isna(df[col].max()) else None, | |
| "mean": float(df[col].mean()) if not pd.isna(df[col].mean()) else None, | |
| "is_numeric": True | |
| }) | |
| else: | |
| col_info["is_numeric"] = False | |
| schema["columns"].append(col_info) | |
| return { | |
| "success": True, | |
| "schema": schema | |
| } | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |
| def convert_column_types( | |
| self, | |
| data: Any, | |
| type_mapping: Dict[str, str] | |
| ) -> Dict[str, Any]: | |
| """ | |
| Convert column data types. | |
| Args: | |
| data: Input data | |
| type_mapping: {column: target_type} mapping | |
| Types: 'int', 'float', 'str', 'datetime', 'bool' | |
| Returns: | |
| Data with converted types | |
| """ | |
| try: | |
| import pandas as pd | |
| if isinstance(data, pd.DataFrame): | |
| df = data.copy() | |
| else: | |
| df = pd.DataFrame(data) | |
| converted = [] | |
| errors = [] | |
| for col, target_type in type_mapping.items(): | |
| if col not in df.columns: | |
| errors.append(f"Column '{col}' not found") | |
| continue | |
| try: | |
| if target_type == 'int': | |
| df[col] = pd.to_numeric(df[col], errors='coerce').astype('Int64') | |
| elif target_type == 'float': | |
| df[col] = pd.to_numeric(df[col], errors='coerce') | |
| elif target_type == 'str': | |
| df[col] = df[col].astype(str) | |
| elif target_type == 'datetime': | |
| df[col] = pd.to_datetime(df[col], errors='coerce') | |
| elif target_type == 'bool': | |
| df[col] = df[col].astype(bool) | |
| converted.append(col) | |
| except Exception as e: | |
| errors.append(f"Error converting '{col}': {str(e)}") | |
| return { | |
| "success": len(errors) == 0, | |
| "data": df.to_dict(orient='records'), | |
| "converted": converted, | |
| "errors": errors if errors else None, | |
| "columns": list(df.columns) | |
| } | |
| except Exception as e: | |
| return {"success": False, "error": str(e)} | |
| # Convenience functions for direct MCP calls | |
| def transform_pivot(data, index, columns, values, aggfunc='sum'): | |
| """Pivot data transformation""" | |
| transformer = DataTransformer() | |
| return transformer.pivot_data(data, index, columns, values, aggfunc) | |
| def transform_aggregate(data, group_by, aggregations): | |
| """Aggregate data transformation""" | |
| transformer = DataTransformer() | |
| return transformer.aggregate_data(data, group_by, aggregations) | |
| def transform_convert(data, source_format, target_format, **options): | |
| """Format conversion""" | |
| transformer = DataTransformer() | |
| return transformer.convert_format(data, source_format, target_format, **options) | |
| def infer_data_schema(data): | |
| """Infer data schema""" | |
| transformer = DataTransformer() | |
| return transformer.infer_schema(data) | |
| # Quick test | |
| if __name__ == "__main__": | |
| test_data = [ | |
| {"product": "A", "region": "North", "sales": 100}, | |
| {"product": "A", "region": "South", "sales": 150}, | |
| {"product": "B", "region": "North", "sales": 200}, | |
| {"product": "B", "region": "South", "sales": 175}, | |
| ] | |
| transformer = DataTransformer() | |
| # Test pivot | |
| result = transformer.pivot_data(test_data, "product", "region", "sales") | |
| print("Pivot Result:", result) | |
| # Test aggregation | |
| result = transformer.aggregate_data( | |
| test_data, | |
| ["product"], | |
| {"sales": "sum"} | |
| ) | |
| print("Aggregate Result:", result) | |
| # Test schema inference | |
| result = transformer.infer_schema(test_data) | |
| print("Schema:", result) | |