|
|
|
|
@@ -1598,27 +1598,6 @@ function removeTracesFromClusterCache(traceIds: number[]) {
|
|
|
|
|
clusterResultsCache.value[selectedFileUuid.value] = validClusters
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateClusterAfterMerge(sourceGroupId: string, targetGroupId: string) {
|
|
|
|
|
const clusters = clusterResultsCache.value[selectedFileUuid.value]
|
|
|
|
|
if (!clusters) return
|
|
|
|
|
|
|
|
|
|
const sourceCluster = clusters.find((c: any) => c.identity_uuid === sourceGroupId)
|
|
|
|
|
const targetCluster = clusters.find((c: any) => c.identity_uuid === targetGroupId)
|
|
|
|
|
|
|
|
|
|
if (sourceCluster && targetCluster) {
|
|
|
|
|
targetCluster.trace_ids = [...targetCluster.trace_ids, ...sourceCluster.trace_ids]
|
|
|
|
|
targetCluster.trace_count = targetCluster.trace_ids.length
|
|
|
|
|
|
|
|
|
|
const idx = clusters.findIndex((c: any) => c.identity_uuid === sourceGroupId)
|
|
|
|
|
if (idx >= 0) {
|
|
|
|
|
clusters.splice(idx, 1) // Remove empty source group from cache
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trigger computed property update
|
|
|
|
|
clusterResultsCache.value = { ...clusterResultsCache.value }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function batchDeleteFaces() {
|
|
|
|
|
if (!selectedFaces.value.length) return
|
|
|
|
|
|
|
|
|
|
@@ -1725,18 +1704,10 @@ async function executeMerge(targetGroupId: string) {
|
|
|
|
|
console.log('[executeMerge] targetGroup:', targetGroup)
|
|
|
|
|
if (!targetGroup) return
|
|
|
|
|
|
|
|
|
|
const targetTraceId = targetGroup.trace_ids?.[0]
|
|
|
|
|
console.log('[executeMerge] targetTraceId:', targetTraceId, typeof targetTraceId)
|
|
|
|
|
if (!targetTraceId) {
|
|
|
|
|
alert('Target group has no traces')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const targetTrace = clusterTracesMap.value[String(targetTraceId)]
|
|
|
|
|
console.log('[executeMerge] targetTrace:', targetTrace)
|
|
|
|
|
const targetFileUuid = targetTrace?.file_uuid
|
|
|
|
|
if (!targetFileUuid) {
|
|
|
|
|
alert('Target trace has no file_uuid')
|
|
|
|
|
const targetGroupName = targetGroup.name
|
|
|
|
|
console.log('[executeMerge] targetGroupName:', targetGroupName)
|
|
|
|
|
if (!targetGroupName) {
|
|
|
|
|
alert('Target group has no name')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1745,71 +1716,48 @@ async function executeMerge(targetGroupId: string) {
|
|
|
|
|
const sourceGroups = clusterAsPending.value.filter((c: any) => sourceGroupIds.includes(c.identity_uuid))
|
|
|
|
|
console.log('[executeMerge] sourceGroups:', sourceGroups.length)
|
|
|
|
|
|
|
|
|
|
let totalTraces = 0
|
|
|
|
|
let validTraces = 0
|
|
|
|
|
for (const g of sourceGroups) {
|
|
|
|
|
for (const tid of g.trace_ids || []) {
|
|
|
|
|
totalTraces++
|
|
|
|
|
const t = clusterTracesMap.value[String(tid)]
|
|
|
|
|
if (t?.file_uuid) validTraces++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const sourceGroupNames = sourceGroups.map(g => g.name).filter(Boolean)
|
|
|
|
|
console.log('[executeMerge] sourceGroupNames:', sourceGroupNames)
|
|
|
|
|
|
|
|
|
|
console.log('[executeMerge] totalTraces:', totalTraces, 'validTraces:', validTraces)
|
|
|
|
|
|
|
|
|
|
if (validTraces === 0) {
|
|
|
|
|
alert('No valid traces to merge (all missing file_uuid)')
|
|
|
|
|
if (sourceGroupNames.length === 0) {
|
|
|
|
|
alert('No valid source groups to merge')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!confirm(`Merge ${validTraces}/${totalTraces} trace(s) from ${sourceGroups.length} group(s)?`)) return
|
|
|
|
|
if (!confirm(`Merge ${sourceGroupNames.length} group(s) into "${targetGroupName}"?`)) return
|
|
|
|
|
|
|
|
|
|
operationLoading.value = true
|
|
|
|
|
operationMessage.value = `Merging ${validTraces} trace(s)...`
|
|
|
|
|
operationMessage.value = `Merging ${sourceGroupNames.length} group(s)...`
|
|
|
|
|
|
|
|
|
|
let successCount = 0
|
|
|
|
|
let failCount = 0
|
|
|
|
|
|
|
|
|
|
for (const g of sourceGroups) {
|
|
|
|
|
for (const srcTraceId of g.trace_ids || []) {
|
|
|
|
|
const srcTrace = clusterTracesMap.value[String(srcTraceId)]
|
|
|
|
|
const srcFileUuid = srcTrace?.file_uuid
|
|
|
|
|
if (!srcFileUuid) {
|
|
|
|
|
console.warn('[merge] skipping trace without file_uuid:', srcTraceId)
|
|
|
|
|
failCount++
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if (srcFileUuid !== targetFileUuid) {
|
|
|
|
|
console.warn('[merge] skipping cross-file trace:', srcTraceId, srcFileUuid, '!=', targetFileUuid)
|
|
|
|
|
failCount++
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
console.log('[executeMerge] calling merge_trace:', {
|
|
|
|
|
file_uuid: srcFileUuid,
|
|
|
|
|
source_id: srcTraceId,
|
|
|
|
|
target_id: targetTraceId
|
|
|
|
|
})
|
|
|
|
|
await apiCall('merge_trace', {
|
|
|
|
|
file_uuid: srcFileUuid,
|
|
|
|
|
source_id: srcTraceId,
|
|
|
|
|
target_id: targetTraceId
|
|
|
|
|
})
|
|
|
|
|
successCount++
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('Merge trace failed:', srcTraceId, e)
|
|
|
|
|
failCount++
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
console.log('[executeMerge] calling merge_groups API:', {
|
|
|
|
|
fileUuid: selectedFileUuid.value,
|
|
|
|
|
sourceGroups: sourceGroupNames,
|
|
|
|
|
targetGroupName: targetGroupName
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const result = await apiCall('merge_groups', {
|
|
|
|
|
fileUuid: selectedFileUuid.value,
|
|
|
|
|
sourceGroups: sourceGroupNames,
|
|
|
|
|
targetGroupName: targetGroupName
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
console.log('[executeMerge] result:', result)
|
|
|
|
|
|
|
|
|
|
if (result?.success) {
|
|
|
|
|
console.log('[executeMerge] success, traces_merged:', result.traces_merged)
|
|
|
|
|
await loadClusterResults()
|
|
|
|
|
clearSelection()
|
|
|
|
|
} else {
|
|
|
|
|
alert(`Merge failed: ${result?.error || 'Unknown error'}`)
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('[executeMerge] error:', e)
|
|
|
|
|
alert(`Merge failed: ${e}`)
|
|
|
|
|
} finally {
|
|
|
|
|
operationLoading.value = false
|
|
|
|
|
operationMessage.value = ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('[executeMerge] completed:', successCount, 'success,', failCount, 'failed')
|
|
|
|
|
|
|
|
|
|
operationLoading.value = false
|
|
|
|
|
operationMessage.value = ''
|
|
|
|
|
|
|
|
|
|
clearSelection()
|
|
|
|
|
await loadClusterResults()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function batchMoveFaces() {
|
|
|
|
|
|