""" 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