gateway/modules/methods/methodExcel.py
2025-06-21 03:06:00 +02:00

426 lines
No EOL
16 KiB
Python

"""
Excel method module.
Handles Excel operations using the Excel service.
"""
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime, UTC
import json
import base64
from modules.workflow.methodBase import MethodBase, ActionResult, action
logger = logging.getLogger(__name__)
class ExcelService:
"""Service for Microsoft Excel operations using Graph API"""
def __init__(self, serviceContainer: Any):
self.serviceContainer = serviceContainer
def _getMicrosoftConnection(self, connectionReference: str) -> Optional[Dict[str, Any]]:
"""Get Microsoft connection from connection reference"""
try:
userConnection = self.serviceContainer.getUserConnectionFromConnectionReference(connectionReference)
if not userConnection or userConnection.authority != "msft" or userConnection.status != "active":
return None
# Get the corresponding token for this user and authority
token = self.serviceContainer.interfaceApp.getToken(userConnection.authority)
if not token:
logger.warning(f"No token found for user {userConnection.userId} and authority {userConnection.authority}")
return None
return {
"id": userConnection.id,
"accessToken": token.tokenAccess,
"refreshToken": token.tokenRefresh,
"scopes": ["Mail.ReadWrite", "User.Read"] # Default Microsoft scopes
}
except Exception as e:
logger.error(f"Error getting Microsoft connection: {str(e)}")
return None
async def readFile(self, fileId: str, connectionReference: str, sheetName: str = "Sheet1", range: str = None) -> Dict[str, Any]:
"""Read data from Excel file using Microsoft Graph API"""
try:
connection = self._getMicrosoftConnection(connectionReference)
if not connection:
return {
"error": "No valid Microsoft connection found for the provided connection reference",
"fileId": fileId,
"connectionReference": connectionReference
}
# Get file data from service container
file_data = self.serviceContainer.getFileData(fileId)
file_info = self.serviceContainer.getFileInfo(fileId)
if not file_data:
return {
"error": "File not found or empty",
"fileId": fileId
}
# For now, simulate Excel reading with AI analysis
# In a real implementation, you would use Microsoft Graph API
excel_prompt = f"""
Analyze this Excel file data and extract structured information.
File: {file_info.get('name', 'Unknown')}
Sheet: {sheetName}
Range: {range or 'All data'}
File content (first 5000 characters):
{file_data.decode('utf-8', errors='ignore')[:5000] if isinstance(file_data, bytes) else str(file_data)[:5000]}
Please extract:
1. All data from the specified sheet and range
2. Column headers and data types
3. Key metrics and calculations
4. Any charts or visualizations described
5. Summary statistics
Return the data in a structured JSON format.
"""
# Use AI to analyze Excel content
analysis_result = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(excel_prompt)
return {
"fileId": fileId,
"sheetName": sheetName,
"range": range,
"data": analysis_result,
"fileInfo": file_info,
"connection": {
"id": connection["id"],
"authority": "microsoft",
"reference": connectionReference
}
}
except Exception as e:
logger.error(f"Error reading Excel file: {str(e)}")
return {
"error": str(e),
"fileId": fileId
}
async def writeFile(self, fileId: str, connectionReference: str, sheetName: str, data: Any, range: str = None) -> Dict[str, Any]:
"""Write data to Excel file using Microsoft Graph API"""
try:
connection = self._getMicrosoftConnection(connectionReference)
if not connection:
return {
"error": "No valid Microsoft connection found for the provided connection reference",
"fileId": fileId,
"connectionReference": connectionReference
}
# For now, simulate Excel writing
# In a real implementation, you would use Microsoft Graph API
write_prompt = f"""
Prepare data for writing to Excel file.
File: {fileId}
Sheet: {sheetName}
Range: {range or 'Auto-detect'}
Data to write:
{json.dumps(data, indent=2)}
Please format this data appropriately for Excel and provide:
1. Structured data ready for Excel
2. Column headers and formatting
3. Any formulas or calculations needed
4. Data validation rules if applicable
"""
# Use AI to prepare Excel data
prepared_data = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(write_prompt)
return {
"fileId": fileId,
"sheetName": sheetName,
"range": range,
"data": prepared_data,
"status": "prepared",
"connection": {
"id": connection["id"],
"authority": "microsoft",
"reference": connectionReference
}
}
except Exception as e:
logger.error(f"Error writing to Excel file: {str(e)}")
return {
"error": str(e),
"fileId": fileId
}
async def createFile(self, fileName: str, connectionReference: str, template: str = None) -> Dict[str, Any]:
"""Create new Excel file using Microsoft Graph API"""
try:
connection = self._getMicrosoftConnection(connectionReference)
if not connection:
return {
"error": "No valid Microsoft connection found for the provided connection reference",
"connectionReference": connectionReference
}
# For now, simulate file creation
# In a real implementation, you would use Microsoft Graph API
create_prompt = f"""
Create a new Excel file structure.
File name: {fileName}
Template: {template or 'Standard'}
Please provide:
1. Initial sheet structure
2. Default column headers
3. Sample data if template specified
4. Formatting guidelines
"""
# Use AI to create Excel structure
file_structure = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(create_prompt)
# Create file using service container
file_id = self.serviceContainer.createFile(
fileName=fileName,
mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
content=file_structure,
base64encoded=False
)
return {
"fileId": file_id,
"fileName": fileName,
"template": template,
"structure": file_structure,
"connection": {
"id": connection["id"],
"authority": "microsoft",
"reference": connectionReference
}
}
except Exception as e:
logger.error(f"Error creating Excel file: {str(e)}")
return {
"error": str(e)
}
async def formatCells(self, fileId: str, connectionReference: str, sheetName: str, range: str, format: Dict[str, Any]) -> Dict[str, Any]:
"""Format Excel cells using Microsoft Graph API"""
try:
connection = self._getMicrosoftConnection(connectionReference)
if not connection:
return {
"error": "No valid Microsoft connection found for the provided connection reference",
"fileId": fileId,
"connectionReference": connectionReference
}
# For now, simulate formatting
# In a real implementation, you would use Microsoft Graph API
format_prompt = f"""
Apply formatting to Excel cells.
File: {fileId}
Sheet: {sheetName}
Range: {range}
Format: {json.dumps(format, indent=2)}
Please provide:
1. Applied formatting details
2. Visual representation of the formatting
3. Any conditional formatting rules
"""
# Use AI to describe formatting
formatting_result = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(format_prompt)
return {
"fileId": fileId,
"sheetName": sheetName,
"range": range,
"format": format,
"result": formatting_result,
"connection": {
"id": connection["id"],
"authority": "microsoft",
"reference": connectionReference
}
}
except Exception as e:
logger.error(f"Error formatting Excel cells: {str(e)}")
return {
"error": str(e),
"fileId": fileId
}
class MethodExcel(MethodBase):
"""Excel method implementation for spreadsheet operations"""
def __init__(self, serviceContainer: Any):
"""Initialize the Excel method"""
super().__init__(serviceContainer)
self.name = "excel"
self.description = "Handle Excel spreadsheet operations like reading and writing data"
self.excelService = ExcelService(serviceContainer)
@action
async def read(self, parameters: Dict[str, Any]) -> ActionResult:
"""Read data from Excel file"""
try:
fileId = parameters.get("fileId")
connectionReference = parameters.get("connectionReference")
sheetName = parameters.get("sheetName", "Sheet1")
range = parameters.get("range")
if not fileId or not connectionReference:
return self._createResult(
success=False,
data={},
error="File ID and connection reference are required"
)
# Read data from Excel
data = await self.excelService.readFile(
fileId=fileId,
connectionReference=connectionReference,
sheetName=sheetName,
range=range
)
return self._createResult(
success=True,
data=data
)
except Exception as e:
logger.error(f"Error reading Excel file: {str(e)}")
return self._createResult(
success=False,
data={},
error=str(e)
)
@action
async def write(self, parameters: Dict[str, Any]) -> ActionResult:
"""Write data to Excel file"""
try:
fileId = parameters.get("fileId")
connectionReference = parameters.get("connectionReference")
sheetName = parameters.get("sheetName", "Sheet1")
data = parameters.get("data")
range = parameters.get("range")
if not fileId or not connectionReference or not data:
return self._createResult(
success=False,
data={},
error="File ID, connection reference, and data are required"
)
# Write data to Excel
result = await self.excelService.writeFile(
fileId=fileId,
connectionReference=connectionReference,
sheetName=sheetName,
data=data,
range=range
)
return self._createResult(
success=True,
data=result
)
except Exception as e:
logger.error(f"Error writing to Excel file: {str(e)}")
return self._createResult(
success=False,
data={},
error=str(e)
)
@action
async def create(self, parameters: Dict[str, Any]) -> ActionResult:
"""Create new Excel file"""
try:
fileName = parameters.get("fileName")
connectionReference = parameters.get("connectionReference")
template = parameters.get("template")
if not fileName or not connectionReference:
return self._createResult(
success=False,
data={},
error="File name and connection reference are required"
)
# Create Excel file
fileId = await self.excelService.createFile(
fileName=fileName,
connectionReference=connectionReference,
template=template
)
return self._createResult(
success=True,
data={"fileId": fileId}
)
except Exception as e:
logger.error(f"Error creating Excel file: {str(e)}")
return self._createResult(
success=False,
data={},
error=str(e)
)
@action
async def format(self, parameters: Dict[str, Any]) -> ActionResult:
"""Format Excel cells"""
try:
fileId = parameters.get("fileId")
connectionReference = parameters.get("connectionReference")
sheetName = parameters.get("sheetName", "Sheet1")
range = parameters.get("range")
format = parameters.get("format")
if not fileId or not connectionReference or not range or not format:
return self._createResult(
success=False,
data={},
error="File ID, connection reference, range, and format are required"
)
# Apply formatting
result = await self.excelService.formatCells(
fileId=fileId,
connectionReference=connectionReference,
sheetName=sheetName,
range=range,
format=format
)
return self._createResult(
success=True,
data=result
)
except Exception as e:
logger.error(f"Error formatting Excel cells: {str(e)}")
return self._createResult(
success=False,
data={},
error=str(e)
)