138 lines
5.9 KiB
Python
138 lines
5.9 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
|
|
import logging
|
|
import json
|
|
from typing import Dict, Any, List
|
|
from modules.features.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)
|
|
|