595 lines
No EOL
22 KiB
Python
595 lines
No EOL
22 KiB
Python
"""
|
|
PowerPoint method module.
|
|
Handles PowerPoint operations using the PowerPoint 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 PowerpointService:
|
|
"""Service for Microsoft PowerPoint 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 readPresentation(self, fileId: str, connectionReference: str, includeSlides: bool = True) -> Dict[str, Any]:
|
|
"""Read PowerPoint presentation 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 PowerPoint reading with AI analysis
|
|
# In a real implementation, you would use Microsoft Graph API
|
|
ppt_prompt = f"""
|
|
Analyze this PowerPoint presentation and extract structured information.
|
|
|
|
File: {file_info.get('name', 'Unknown')}
|
|
Include slides: {includeSlides}
|
|
|
|
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. Presentation title and theme
|
|
2. Slide structure and content
|
|
3. Text content from each slide
|
|
4. Images and media references
|
|
5. Charts and data visualizations
|
|
6. Speaker notes if available
|
|
7. Overall presentation flow and messaging
|
|
|
|
Return the data in a structured JSON format.
|
|
"""
|
|
|
|
# Use AI to analyze PowerPoint content
|
|
analysis_result = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(ppt_prompt)
|
|
|
|
return {
|
|
"fileId": fileId,
|
|
"includeSlides": includeSlides,
|
|
"data": analysis_result,
|
|
"fileInfo": file_info,
|
|
"connection": {
|
|
"id": connection["id"],
|
|
"authority": "microsoft",
|
|
"reference": connectionReference
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error reading presentation: {str(e)}")
|
|
return {
|
|
"error": str(e),
|
|
"fileId": fileId
|
|
}
|
|
|
|
async def writePresentation(self, fileId: str, connectionReference: str, slides: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
"""Write to PowerPoint presentation 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 PowerPoint writing
|
|
# In a real implementation, you would use Microsoft Graph API
|
|
write_prompt = f"""
|
|
Prepare content for writing to PowerPoint presentation.
|
|
|
|
File: {fileId}
|
|
Number of slides: {len(slides)}
|
|
|
|
Slides data:
|
|
{json.dumps(slides, indent=2)}
|
|
|
|
Please format this content appropriately for PowerPoint and provide:
|
|
1. Slide layouts and structures
|
|
2. Text content and formatting
|
|
3. Image and media placement
|
|
4. Chart and visualization specifications
|
|
5. Animation and transition suggestions
|
|
"""
|
|
|
|
# Use AI to prepare PowerPoint content
|
|
prepared_content = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(write_prompt)
|
|
|
|
return {
|
|
"fileId": fileId,
|
|
"slides": slides,
|
|
"content": prepared_content,
|
|
"status": "prepared",
|
|
"connection": {
|
|
"id": connection["id"],
|
|
"authority": "microsoft",
|
|
"reference": connectionReference
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error writing to presentation: {str(e)}")
|
|
return {
|
|
"error": str(e),
|
|
"fileId": fileId
|
|
}
|
|
|
|
async def convertPresentation(self, fileId: str, connectionReference: str, format: str = "pdf") -> Dict[str, Any]:
|
|
"""Convert PowerPoint presentation to another format 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 conversion
|
|
# In a real implementation, you would use Microsoft Graph API
|
|
convert_prompt = f"""
|
|
Convert PowerPoint presentation to {format.upper()} format.
|
|
|
|
File: {fileId}
|
|
Target format: {format}
|
|
|
|
Please provide:
|
|
1. Conversion specifications
|
|
2. Format-specific optimizations
|
|
3. Quality settings and options
|
|
4. Any special considerations for the target format
|
|
"""
|
|
|
|
# Use AI to describe conversion process
|
|
conversion_result = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(convert_prompt)
|
|
|
|
# Create converted file using service container
|
|
converted_file_id = self.serviceContainer.createFile(
|
|
fileName=f"converted_presentation.{format}",
|
|
mimeType=f"application/{format}",
|
|
content=conversion_result,
|
|
base64encoded=False
|
|
)
|
|
|
|
return {
|
|
"fileId": fileId,
|
|
"format": format,
|
|
"convertedFileId": converted_file_id,
|
|
"result": conversion_result,
|
|
"connection": {
|
|
"id": connection["id"],
|
|
"authority": "microsoft",
|
|
"reference": connectionReference
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error converting presentation: {str(e)}")
|
|
return {
|
|
"error": str(e),
|
|
"fileId": fileId
|
|
}
|
|
|
|
async def createPresentation(self, fileName: str, connectionReference: str, template: str = None) -> Dict[str, Any]:
|
|
"""Create new PowerPoint presentation 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 presentation creation
|
|
# In a real implementation, you would use Microsoft Graph API
|
|
create_prompt = f"""
|
|
Create a new PowerPoint presentation structure.
|
|
|
|
File name: {fileName}
|
|
Template: {template or 'Standard'}
|
|
|
|
Please provide:
|
|
1. Initial slide structure
|
|
2. Default slide layouts
|
|
3. Theme and design elements
|
|
4. Sample content if template specified
|
|
5. Presentation guidelines
|
|
"""
|
|
|
|
# Use AI to create PowerPoint structure
|
|
presentation_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.presentationml.presentation",
|
|
content=presentation_structure,
|
|
base64encoded=False
|
|
)
|
|
|
|
return {
|
|
"fileId": file_id,
|
|
"fileName": fileName,
|
|
"template": template,
|
|
"structure": presentation_structure,
|
|
"connection": {
|
|
"id": connection["id"],
|
|
"authority": "microsoft",
|
|
"reference": connectionReference
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error creating presentation: {str(e)}")
|
|
return {
|
|
"error": str(e)
|
|
}
|
|
|
|
async def addSlide(self, fileId: str, connectionReference: str, layout: str = "title", content: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
"""Add slide to presentation 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 slide addition
|
|
# In a real implementation, you would use Microsoft Graph API
|
|
slide_prompt = f"""
|
|
Add a new slide to PowerPoint presentation.
|
|
|
|
File: {fileId}
|
|
Layout: {layout}
|
|
Content: {json.dumps(content, indent=2) if content else 'Default content'}
|
|
|
|
Please provide:
|
|
1. Slide structure and layout
|
|
2. Content placement and formatting
|
|
3. Visual elements and design
|
|
4. Slide number and positioning
|
|
"""
|
|
|
|
# Use AI to create slide content
|
|
slide_content = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(slide_prompt)
|
|
|
|
return {
|
|
"fileId": fileId,
|
|
"layout": layout,
|
|
"content": content,
|
|
"slideContent": slide_content,
|
|
"connection": {
|
|
"id": connection["id"],
|
|
"authority": "microsoft",
|
|
"reference": connectionReference
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error adding slide: {str(e)}")
|
|
return {
|
|
"error": str(e),
|
|
"fileId": fileId
|
|
}
|
|
|
|
async def addContent(self, fileId: str, connectionReference: str, slideId: str, content: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Add content to slide 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 content addition
|
|
# In a real implementation, you would use Microsoft Graph API
|
|
content_prompt = f"""
|
|
Add content to PowerPoint slide.
|
|
|
|
File: {fileId}
|
|
Slide ID: {slideId}
|
|
Content: {json.dumps(content, indent=2)}
|
|
|
|
Please provide:
|
|
1. Content placement and formatting
|
|
2. Text styling and layout
|
|
3. Image and media integration
|
|
4. Chart and visualization setup
|
|
5. Animation and effects
|
|
"""
|
|
|
|
# Use AI to format slide content
|
|
formatted_content = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(content_prompt)
|
|
|
|
return {
|
|
"fileId": fileId,
|
|
"slideId": slideId,
|
|
"content": content,
|
|
"formattedContent": formatted_content,
|
|
"connection": {
|
|
"id": connection["id"],
|
|
"authority": "microsoft",
|
|
"reference": connectionReference
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error adding content: {str(e)}")
|
|
return {
|
|
"error": str(e),
|
|
"fileId": fileId
|
|
}
|
|
|
|
class MethodPowerpoint(MethodBase):
|
|
"""PowerPoint method implementation for presentation operations"""
|
|
|
|
def __init__(self, serviceContainer: Any):
|
|
"""Initialize the PowerPoint method"""
|
|
super().__init__(serviceContainer)
|
|
self.name = "powerpoint"
|
|
self.description = "Handle PowerPoint presentation operations like reading and creating slides"
|
|
self.powerpointService = PowerpointService(serviceContainer)
|
|
|
|
@action
|
|
async def read(self, parameters: Dict[str, Any]) -> ActionResult:
|
|
"""Read PowerPoint presentation"""
|
|
try:
|
|
fileId = parameters.get("fileId")
|
|
connectionReference = parameters.get("connectionReference")
|
|
includeSlides = parameters.get("includeSlides", True)
|
|
|
|
if not fileId or not connectionReference:
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error="File ID and connection reference are required"
|
|
)
|
|
|
|
# Read presentation
|
|
data = await self.powerpointService.readPresentation(
|
|
fileId=fileId,
|
|
connectionReference=connectionReference,
|
|
includeSlides=includeSlides
|
|
)
|
|
|
|
return self._createResult(
|
|
success=True,
|
|
data=data
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error reading presentation: {str(e)}")
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error=str(e)
|
|
)
|
|
|
|
@action
|
|
async def write(self, parameters: Dict[str, Any]) -> ActionResult:
|
|
"""Write to PowerPoint presentation"""
|
|
try:
|
|
fileId = parameters.get("fileId")
|
|
connectionReference = parameters.get("connectionReference")
|
|
slides = parameters.get("slides", [])
|
|
|
|
if not fileId or not connectionReference:
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error="File ID and connection reference are required"
|
|
)
|
|
|
|
# Write to presentation
|
|
result = await self.powerpointService.writePresentation(
|
|
fileId=fileId,
|
|
connectionReference=connectionReference,
|
|
slides=slides
|
|
)
|
|
|
|
return self._createResult(
|
|
success=True,
|
|
data=result
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error writing to presentation: {str(e)}")
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error=str(e)
|
|
)
|
|
|
|
@action
|
|
async def convert(self, parameters: Dict[str, Any]) -> ActionResult:
|
|
"""Convert PowerPoint presentation to another format"""
|
|
try:
|
|
fileId = parameters.get("fileId")
|
|
connectionReference = parameters.get("connectionReference")
|
|
format = parameters.get("format", "pdf")
|
|
|
|
if not fileId or not connectionReference:
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error="File ID and connection reference are required"
|
|
)
|
|
|
|
# Convert presentation
|
|
result = await self.powerpointService.convertPresentation(
|
|
fileId=fileId,
|
|
connectionReference=connectionReference,
|
|
format=format
|
|
)
|
|
|
|
return self._createResult(
|
|
success=True,
|
|
data=result
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error converting presentation: {str(e)}")
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error=str(e)
|
|
)
|
|
|
|
@action
|
|
async def createPresentation(self, parameters: Dict[str, Any]) -> ActionResult:
|
|
"""Create new PowerPoint presentation"""
|
|
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 presentation
|
|
fileId = await self.powerpointService.createPresentation(
|
|
fileName=fileName,
|
|
connectionReference=connectionReference,
|
|
template=template
|
|
)
|
|
|
|
return self._createResult(
|
|
success=True,
|
|
data={"fileId": fileId}
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error creating presentation: {str(e)}")
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error=str(e)
|
|
)
|
|
|
|
@action
|
|
async def addSlide(self, parameters: Dict[str, Any]) -> ActionResult:
|
|
"""Add slide to presentation"""
|
|
try:
|
|
fileId = parameters.get("fileId")
|
|
connectionReference = parameters.get("connectionReference")
|
|
layout = parameters.get("layout", "title")
|
|
content = parameters.get("content", {})
|
|
|
|
if not fileId or not connectionReference:
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error="File ID and connection reference are required"
|
|
)
|
|
|
|
# Add slide
|
|
slide = await self.powerpointService.addSlide(
|
|
fileId=fileId,
|
|
connectionReference=connectionReference,
|
|
layout=layout,
|
|
content=content
|
|
)
|
|
|
|
return self._createResult(
|
|
success=True,
|
|
data=slide
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error adding slide: {str(e)}")
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error=str(e)
|
|
)
|
|
|
|
@action
|
|
async def addContent(self, parameters: Dict[str, Any]) -> ActionResult:
|
|
"""Add content to slide"""
|
|
try:
|
|
fileId = parameters.get("fileId")
|
|
connectionReference = parameters.get("connectionReference")
|
|
slideId = parameters.get("slideId")
|
|
content = parameters.get("content", {})
|
|
|
|
if not fileId or not connectionReference or not slideId:
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error="File ID, connection reference, and slide ID are required"
|
|
)
|
|
|
|
# Add content
|
|
result = await self.powerpointService.addContent(
|
|
fileId=fileId,
|
|
connectionReference=connectionReference,
|
|
slideId=slideId,
|
|
content=content
|
|
)
|
|
|
|
return self._createResult(
|
|
success=True,
|
|
data=result
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error adding content: {str(e)}")
|
|
return self._createResult(
|
|
success=False,
|
|
data={},
|
|
error=str(e)
|
|
) |