426 lines
No EOL
16 KiB
Python
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)
|
|
) |