# Copyright (c) 2025 Patrick Motsch # All rights reserved. import logging import json from typing import Dict, Any, List from modules.aichat.datamodelFeatureAiChat import ActionResult, ActionDocument logger = logging.getLogger(__name__) async def mergeTicketData(self, parameters: Dict[str, Any]) -> ActionResult: try: jiraDataParam = parameters.get("jiraData") if not jiraDataParam: return ActionResult.isFailure(error="jiraData parameter is required") existingDataParam = parameters.get("existingData") if not existingDataParam: return ActionResult.isFailure(error="existingData parameter is required") taskSyncDefinitionParam = parameters.get("taskSyncDefinition") if not taskSyncDefinitionParam: return ActionResult.isFailure(error="taskSyncDefinition parameter is required") idField = parameters.get("idField", "ID") # Parse taskSyncDefinition if isinstance(taskSyncDefinitionParam, str): try: taskSyncDefinition = json.loads(taskSyncDefinitionParam) except json.JSONDecodeError as e: return ActionResult.isFailure(error=f"taskSyncDefinition is not valid JSON: {str(e)}") elif isinstance(taskSyncDefinitionParam, dict): taskSyncDefinition = taskSyncDefinitionParam else: return ActionResult.isFailure(error=f"taskSyncDefinition must be a dict or JSON string, got {type(taskSyncDefinitionParam)}") # Get data from documents jiraDataJson = self.documentParsing.parseJsonFromDocument(jiraDataParam) if jiraDataJson is None or not isinstance(jiraDataJson, list): return ActionResult.isFailure(error="Could not parse jiraData as JSON array") existingDataJson = self.documentParsing.parseJsonFromDocument(existingDataParam) if existingDataJson is None or not isinstance(existingDataJson, list): # Empty existing data is OK existingDataJson = [] # Perform merge existingLookup = {row.get(idField): row for row in existingDataJson if row.get(idField)} mergedData: List[dict] = [] changes: List[str] = [] updatedCount = addedCount = unchangedCount = 0 for jiraRow in jiraDataJson: jiraId = jiraRow.get(idField) if jiraId and jiraId in existingLookup: existingRow = existingLookup[jiraId].copy() rowChanges: List[str] = [] for fieldName, fieldConfig in taskSyncDefinition.items(): if fieldConfig[0] == 'get': oldValue = "" if existingRow.get(fieldName) is None else str(existingRow.get(fieldName)) newValue = "" if jiraRow.get(fieldName) is None else str(jiraRow.get(fieldName)) # Convert ADF data to readable text for logging if isinstance(newValue, dict) and newValue.get("type") == "doc": newValueReadable = self.adfConverter.convertAdfToText(newValue) if oldValue != newValueReadable: rowChanges.append(f"{fieldName}: '{oldValue[:100]}...' -> '{newValueReadable[:100]}...'") elif oldValue != newValue: # Truncate long values for logging oldTruncated = oldValue[:100] + "..." if len(oldValue) > 100 else oldValue newTruncated = newValue[:100] + "..." if len(newValue) > 100 else newValue rowChanges.append(f"{fieldName}: '{oldTruncated}' -> '{newTruncated}'") existingRow[fieldName] = jiraRow.get(fieldName) mergedData.append(existingRow) if rowChanges: updatedCount += 1 changes.append(f"Row ID {jiraId} updated: {', '.join(rowChanges)}") else: unchangedCount += 1 del existingLookup[jiraId] else: mergedData.append(jiraRow) addedCount += 1 changes.append(f"Row ID {jiraId} added as new record") # Add remaining existing rows for remaining in existingLookup.values(): mergedData.append(remaining) unchangedCount += 1 mergeDetails = { "updated": updatedCount, "added": addedCount, "unchanged": unchangedCount, "changes": changes } logger.info(f"Merged ticket data: {updatedCount} updated, {addedCount} added, {unchangedCount} unchanged") # Generate filename workflowContext = self.services.chat.getWorkflowContext() if hasattr(self.services, 'chat') else None filename = self._generateMeaningfulFileName( "merged_ticket_data", "json", workflowContext, "mergeTicketData" ) result = { "data": mergedData, "mergeDetails": mergeDetails } validationMetadata = self._createValidationMetadata( "mergeTicketData", updated=updatedCount, added=addedCount, unchanged=unchangedCount ) document = ActionDocument( documentName=filename, documentData=json.dumps(result, indent=2, ensure_ascii=False), mimeType="application/json", validationMetadata=validationMetadata ) return ActionResult.isSuccess(documents=[document]) except Exception as e: errorMsg = f"Error merging ticket data: {str(e)}" logger.error(errorMsg) return ActionResult.isFailure(error=errorMsg)