feat: Implement Advoware Document Sync Handler

- Added advoware_document_sync_step.py to handle 3-way merge sync for documents.
- Introduced locking mechanism for per-Akte synchronization to allow parallel processing.
- Integrated data fetching from EspoCRM, Windows files, and Advoware history.
- Implemented 3-way merge logic for document synchronization and metadata updates.
- Triggered document preview generation for new/changed documents.

feat: Create Shared Steps Module

- Added shared/__init__.py for shared steps across multiple modules.
- Introduced generate_document_preview_step.py for generating document previews.
- Implemented logic to download documents, generate previews, and upload to EspoCRM.

feat: Add VMH Document xAI Sync Handler

- Created document_xai_sync_step.py to manage document synchronization with xAI collections.
- Handled create, update, and delete actions for documents in EspoCRM.
- Integrated logic for triggering preview generation and managing xAI collections.
- Implemented error handling and logging for synchronization processes.
This commit is contained in:
bsiggel
2026-03-26 01:00:49 +00:00
parent d78a4ee67e
commit 86ec4db9db
6 changed files with 279 additions and 106 deletions

View File

@@ -1,12 +1,16 @@
"""
VMH Document Sync Handler
VMH Document xAI Sync Handler
Zentraler Sync-Handler für Documents mit xAI Collections
Zentraler Sync-Handler für Documents mit xAI Collections.
Triggers preview generation for new/changed files.
Verarbeitet:
- vmh.document.create: Neu in EspoCRM Prüfe ob xAI-Sync nötig
- vmh.document.update: Geändert in EspoCRM Prüfe ob xAI-Sync/Update nötig
- vmh.document.delete: Gelöscht in EspoCRM Remove from xAI Collections
Enqueues:
- document.generate_preview: Bei new/changed Status
"""
from typing import Dict, Any
@@ -19,7 +23,7 @@ import hashlib
import json
config = {
"name": "VMH Document Sync Handler",
"name": "VMH Document xAI Sync Handler",
"description": "Zentraler Sync-Handler für Documents mit xAI Collections",
"flows": ["vmh-documents"],
"triggers": [
@@ -27,7 +31,7 @@ config = {
queue("vmh.document.update"),
queue("vmh.document.delete")
],
"enqueues": []
"enqueues": ["document.generate_preview"]
}
@@ -197,83 +201,21 @@ async def handle_create_or_update(entity_id: str, document: Dict[str, Any], sync
if datei_status_lower in ['neu', 'geändert', 'new', 'changed']:
ctx.logger.info("")
ctx.logger.info("=" * 80)
ctx.logger.info("🖼️ PREVIEW-GENERIERUNG STARTEN")
ctx.logger.info("🖼️ TRIGGER PREVIEW-GENERIERUNG")
ctx.logger.info(f" Datei-Status: {datei_status}")
ctx.logger.info("=" * 80)
try:
# 1. Hole Download-Informationen
download_info = await sync_utils.get_document_download_info(entity_id, entity_type)
if not download_info:
ctx.logger.warn("⚠️ Keine Download-Info verfügbar - überspringe Preview")
else:
ctx.logger.info(f"📥 Datei-Info:")
ctx.logger.info(f" Filename: {download_info['filename']}")
ctx.logger.info(f" MIME-Type: {download_info['mime_type']}")
ctx.logger.info(f" Size: {download_info['size']} bytes")
# 2. Download File von EspoCRM
ctx.logger.info(f"📥 Downloading file...")
espocrm = sync_utils.espocrm
file_content = await espocrm.download_attachment(download_info['attachment_id'])
ctx.logger.info(f"✅ Downloaded {len(file_content)} bytes")
# 3. Speichere temporär für Preview-Generierung
import tempfile
import os
with tempfile.NamedTemporaryFile(delete=False, suffix=f"_{download_info['filename']}") as tmp_file:
tmp_file.write(file_content)
tmp_path = tmp_file.name
try:
# 4. Generiere Preview
ctx.logger.info(f"🖼️ Generating preview (600x800 WebP)...")
preview_data = await sync_utils.generate_thumbnail(
tmp_path,
download_info['mime_type'],
max_width=600,
max_height=800
)
if preview_data:
ctx.logger.info(f"✅ Preview generated: {len(preview_data)} bytes WebP")
# 5. Upload Preview zu EspoCRM und reset file status
ctx.logger.info(f"📤 Uploading preview to EspoCRM...")
await sync_utils.update_sync_metadata(
entity_id,
preview_data=preview_data,
reset_file_status=True, # Reset status nach Preview-Generierung
entity_type=entity_type
)
ctx.logger.info(f"✅ Preview uploaded successfully")
else:
ctx.logger.warn("⚠️ Preview-Generierung lieferte keine Daten")
# Auch bei fehlgeschlagener Preview-Generierung Status zurücksetzen
await sync_utils.update_sync_metadata(
entity_id,
reset_file_status=True,
entity_type=entity_type
)
finally:
# Cleanup temp file
try:
os.remove(tmp_path)
except:
pass
# Enqueue preview generation event
await ctx.emit('document.generate_preview', {
'entity_id': entity_id,
'entity_type': entity_type
})
ctx.logger.info(f"✅ Preview generation event emitted for {entity_id}")
except Exception as e:
ctx.logger.error(f"❌ Fehler bei Preview-Generierung: {e}")
import traceback
ctx.logger.error(traceback.format_exc())
ctx.logger.error(f"❌ Fehler beim Triggern der Preview-Generierung: {e}")
# Continue - Preview ist optional
ctx.logger.info("")
ctx.logger.info("=" * 80)
ctx.logger.info("✅ PREVIEW-VERARBEITUNG ABGESCHLOSSEN")
ctx.logger.info("=" * 80)
# ═══════════════════════════════════════════════════════════════