#!/usr/bin/env python3 """ Test Script: Preview Image Upload zu EspoCRM Testet das Hochladen eines Preview-Bildes (WebP) als Attachment zu einem CDokumente Entity via EspoCRM API. Usage: python test_preview_upload.py Example: python test_preview_upload.py 69a68906ac3d0fd25 """ import asyncio import aiohttp import base64 import os import sys from io import BytesIO from PIL import Image # EspoCRM Config (aus Environment oder hardcoded für Test) ESPOCRM_API_BASE_URL = os.getenv('ESPOCRM_API_BASE_URL', 'https://crm.bitbylaw.com/api/v1') ESPOCRM_API_KEY = os.getenv('ESPOCRM_API_KEY', '') # Test-Parameter ENTITY_TYPE = 'CDokumente' FIELD_NAME = 'preview' def generate_test_webp(text: str = "TEST PREVIEW", size: tuple = (600, 800)) -> bytes: """ Generiert ein einfaches Test-WebP-Bild Args: text: Text der im Bild angezeigt wird size: Größe des Bildes (width, height) Returns: WebP image als bytes """ print(f"📐 Generating test image ({size[0]}x{size[1]})...") # Erstelle einfaches Bild mit Text img = Image.new('RGB', size, color='lightblue') # Optional: Füge Text hinzu (benötigt PIL ImageDraw) try: from PIL import ImageDraw, ImageFont draw = ImageDraw.Draw(img) # Versuche ein größeres Font zu laden try: font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 40) except: font = ImageFont.load_default() # Text zentriert bbox = draw.textbbox((0, 0), text, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] x = (size[0] - text_width) // 2 y = (size[1] - text_height) // 2 draw.text((x, y), text, fill='darkblue', font=font) except Exception as e: print(f"⚠️ Text rendering failed: {e}") # Konvertiere zu WebP buffer = BytesIO() img.save(buffer, format='WEBP', quality=85) webp_bytes = buffer.getvalue() print(f"✅ Test image generated: {len(webp_bytes)} bytes") return webp_bytes async def upload_preview_to_espocrm( document_id: str, preview_data: bytes, entity_type: str = 'CDokumente' ) -> dict: """ Upload Preview zu EspoCRM Attachment API Args: document_id: ID des CDokumente/Document Entity preview_data: WebP image als bytes entity_type: Entity-Type (CDokumente oder Document) Returns: Response dict mit Attachment ID """ print(f"\n📤 Uploading preview to {entity_type}/{document_id}...") print(f" Preview size: {len(preview_data)} bytes") # Base64-encode base64_data = base64.b64encode(preview_data).decode('ascii') file_data_uri = f"data:image/webp;base64,{base64_data}" print(f" Base64 encoded: {len(base64_data)} chars") # API Request url = ESPOCRM_API_BASE_URL.rstrip('/') + '/Attachment' headers = { 'X-Api-Key': ESPOCRM_API_KEY, 'Content-Type': 'application/json' } payload = { 'name': 'preview.webp', 'type': 'image/webp', 'role': 'Attachment', 'field': FIELD_NAME, 'relatedType': entity_type, 'relatedId': document_id, 'file': file_data_uri } print(f"\n🌐 POST {url}") print(f" Headers: X-Api-Key={ESPOCRM_API_KEY[:20]}...") print(f" Payload keys: {list(payload.keys())}") print(f" - name: {payload['name']}") print(f" - type: {payload['type']}") print(f" - role: {payload['role']}") print(f" - field: {payload['field']}") print(f" - relatedType: {payload['relatedType']}") print(f" - relatedId: {payload['relatedId']}") print(f" - file: data:image/webp;base64,... ({len(base64_data)} chars)") timeout = aiohttp.ClientTimeout(total=30) async with aiohttp.ClientSession(timeout=timeout) as session: async with session.post(url, headers=headers, json=payload) as response: print(f"\n📥 Response Status: {response.status}") print(f" Content-Type: {response.content_type}") response_text = await response.text() if response.status >= 400: print(f"\n❌ Upload FAILED!") print(f" Status: {response.status}") print(f" Response: {response_text}") raise Exception(f"Upload error {response.status}: {response_text}") # Parse JSON response result = await response.json() attachment_id = result.get('id') print(f"\n✅ Upload SUCCESSFUL!") print(f" Attachment ID: {attachment_id}") print(f" Full response: {result}") return result async def update_entity_with_preview( document_id: str, attachment_id: str, entity_type: str = 'CDokumente' ) -> dict: """ Update Entity mit previewId und previewName Args: document_id: Entity ID attachment_id: Attachment ID vom Upload entity_type: Entity-Type Returns: Updated entity data """ print(f"\n📝 Updating {entity_type}/{document_id} with previewId...") url = f"{ESPOCRM_API_BASE_URL.rstrip('/')}/{entity_type}/{document_id}" headers = { 'X-Api-Key': ESPOCRM_API_KEY, 'Content-Type': 'application/json' } payload = { 'previewId': attachment_id, 'previewName': 'preview.webp' } print(f" PUT {url}") print(f" Payload: {payload}") timeout = aiohttp.ClientTimeout(total=30) async with aiohttp.ClientSession(timeout=timeout) as session: async with session.put(url, headers=headers, json=payload) as response: print(f" Response Status: {response.status}") if response.status >= 400: response_text = await response.text() print(f"\n❌ Update FAILED!") print(f" Status: {response.status}") print(f" Response: {response_text}") raise Exception(f"Update error {response.status}: {response_text}") result = await response.json() print(f"\n✅ Entity updated successfully!") print(f" previewId: {result.get('previewId')}") print(f" previewName: {result.get('previewName')}") return result async def main(): """Main test flow""" print("=" * 80) print("🖼️ ESPOCRM PREVIEW UPLOAD TEST") print("=" * 80) # Check arguments if len(sys.argv) < 2: print("\n❌ Error: Document ID required!") print(f"\nUsage: {sys.argv[0]} ") print(f"Example: {sys.argv[0]} 69a68906ac3d0fd25") sys.exit(1) document_id = sys.argv[1] # Check API key if not ESPOCRM_API_KEY: print("\n❌ Error: ESPOCRM_API_KEY environment variable not set!") sys.exit(1) print(f"\n📋 Test Parameters:") print(f" API Base URL: {ESPOCRM_API_BASE_URL}") print(f" API Key: {ESPOCRM_API_KEY[:20]}...") print(f" Entity Type: {ENTITY_TYPE}") print(f" Document ID: {document_id}") print(f" Field: {FIELD_NAME}") try: # Step 1: Generate test image print("\n" + "=" * 80) print("STEP 1: Generate Test Image") print("=" * 80) preview_data = generate_test_webp(f"Preview Test\n{document_id[:8]}", size=(600, 800)) # Step 2: Upload to EspoCRM print("\n" + "=" * 80) print("STEP 2: Upload to EspoCRM Attachment API") print("=" * 80) result = await upload_preview_to_espocrm(document_id, preview_data, ENTITY_TYPE) attachment_id = result.get('id') # Step 3: Update Entity print("\n" + "=" * 80) print("STEP 3: Update Entity with Preview Reference") print("=" * 80) await update_entity_with_preview(document_id, attachment_id, ENTITY_TYPE) # Success summary print("\n" + "=" * 80) print("✅ TEST SUCCESSFUL!") print("=" * 80) print(f"\n📊 Summary:") print(f" - Attachment ID: {attachment_id}") print(f" - Entity: {ENTITY_TYPE}/{document_id}") print(f" - Preview Size: {len(preview_data)} bytes") print(f"\n🔗 View in EspoCRM:") print(f" {ESPOCRM_API_BASE_URL.replace('/api/v1', '')}/#CDokumente/view/{document_id}") except Exception as e: print("\n" + "=" * 80) print("❌ TEST FAILED!") print("=" * 80) print(f"\nError: {e}") import traceback traceback.print_exc() sys.exit(1) if __name__ == '__main__': asyncio.run(main())