- Implemented create, update, and delete webhook handlers for Beteiligte. - Implemented create, update, and delete webhook handlers for Document entities. - Added logging and error handling for each webhook handler. - Created a universal step for generating document previews. - Ensured payload validation and entity ID extraction for batch processing.
131 lines
4.7 KiB
Python
131 lines
4.7 KiB
Python
"""
|
|
Generate Document Preview Step
|
|
|
|
Universal step for generating document previews.
|
|
Can be triggered by any document sync flow.
|
|
|
|
Flow:
|
|
1. Load document from EspoCRM
|
|
2. Download file attachment
|
|
3. Generate preview (PDF, DOCX, Images → WebP)
|
|
4. Upload preview to EspoCRM
|
|
5. Update document metadata
|
|
|
|
Event: document.generate_preview
|
|
Input: entity_id, entity_type (default: 'CDokumente')
|
|
"""
|
|
|
|
from typing import Dict, Any
|
|
from motia import FlowContext, queue
|
|
import tempfile
|
|
import os
|
|
|
|
|
|
config = {
|
|
"name": "Generate Document Preview",
|
|
"description": "Generates preview image for documents",
|
|
"flows": ["document-preview"],
|
|
"triggers": [queue("document.generate_preview")],
|
|
"enqueues": [],
|
|
}
|
|
|
|
|
|
async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]) -> None:
|
|
"""
|
|
Generate preview for a document.
|
|
|
|
Args:
|
|
event_data: {
|
|
'entity_id': str, # Required: Document ID
|
|
'entity_type': str, # Optional: 'CDokumente' (default) or 'Document'
|
|
}
|
|
"""
|
|
from services.document_sync_utils import DocumentSync
|
|
|
|
entity_id = event_data.get('entity_id')
|
|
entity_type = event_data.get('entity_type', 'CDokumente')
|
|
|
|
if not entity_id:
|
|
ctx.logger.error("❌ Missing entity_id in event data")
|
|
return
|
|
|
|
ctx.logger.info("=" * 80)
|
|
ctx.logger.info(f"🖼️ GENERATE DOCUMENT PREVIEW")
|
|
ctx.logger.info("=" * 80)
|
|
ctx.logger.info(f"Entity Type: {entity_type}")
|
|
ctx.logger.info(f"Document ID: {entity_id}")
|
|
ctx.logger.info("=" * 80)
|
|
|
|
# Initialize sync utils
|
|
sync_utils = DocumentSync(ctx)
|
|
|
|
try:
|
|
# Step 1: Get download info from EspoCRM
|
|
ctx.logger.info("📥 Step 1: Getting download info from EspoCRM...")
|
|
download_info = await sync_utils.get_document_download_info(entity_id, entity_type)
|
|
|
|
if not download_info:
|
|
ctx.logger.warn("⚠️ No download info available - skipping preview generation")
|
|
return
|
|
|
|
attachment_id = download_info['attachment_id']
|
|
filename = download_info['filename']
|
|
mime_type = download_info['mime_type']
|
|
|
|
ctx.logger.info(f" Filename: {filename}")
|
|
ctx.logger.info(f" MIME Type: {mime_type}")
|
|
ctx.logger.info(f" Attachment ID: {attachment_id}")
|
|
|
|
# Step 2: Download file from EspoCRM
|
|
ctx.logger.info("📥 Step 2: Downloading file from EspoCRM...")
|
|
file_content = await sync_utils.espocrm.download_attachment(attachment_id)
|
|
ctx.logger.info(f" Downloaded: {len(file_content)} bytes")
|
|
|
|
# Step 3: Save to temporary file for preview generation
|
|
ctx.logger.info("💾 Step 3: Saving to temporary file...")
|
|
with tempfile.NamedTemporaryFile(mode='wb', delete=False, suffix=os.path.splitext(filename)[1]) as tmp_file:
|
|
tmp_file.write(file_content)
|
|
tmp_path = tmp_file.name
|
|
|
|
try:
|
|
# Step 4: Generate preview (600x800 WebP)
|
|
ctx.logger.info(f"🖼️ Step 4: Generating preview (600x800 WebP)...")
|
|
preview_data = await sync_utils.generate_thumbnail(
|
|
tmp_path,
|
|
mime_type,
|
|
max_width=600,
|
|
max_height=800
|
|
)
|
|
|
|
if preview_data:
|
|
ctx.logger.info(f"✅ Preview generated: {len(preview_data)} bytes WebP")
|
|
|
|
# Step 5: Upload preview to EspoCRM
|
|
ctx.logger.info(f"📤 Step 5: Uploading preview to EspoCRM...")
|
|
await sync_utils._upload_preview_to_espocrm(entity_id, preview_data, entity_type)
|
|
ctx.logger.info(f"✅ Preview uploaded successfully")
|
|
|
|
ctx.logger.info("=" * 80)
|
|
ctx.logger.info("✅ PREVIEW GENERATION COMPLETE")
|
|
ctx.logger.info("=" * 80)
|
|
else:
|
|
ctx.logger.warn("⚠️ Preview generation returned no data")
|
|
ctx.logger.info("=" * 80)
|
|
ctx.logger.info("⚠️ PREVIEW GENERATION FAILED")
|
|
ctx.logger.info("=" * 80)
|
|
|
|
finally:
|
|
# Cleanup temporary file
|
|
if os.path.exists(tmp_path):
|
|
os.remove(tmp_path)
|
|
ctx.logger.debug(f"🗑️ Removed temporary file: {tmp_path}")
|
|
|
|
except Exception as e:
|
|
ctx.logger.error(f"❌ Preview generation failed: {e}")
|
|
ctx.logger.info("=" * 80)
|
|
ctx.logger.info("❌ PREVIEW GENERATION ERROR")
|
|
ctx.logger.info("=" * 80)
|
|
import traceback
|
|
ctx.logger.debug(traceback.format_exc())
|
|
# Don't raise - preview generation is optional
|