import type { Dataset, FullComparisonResult } from '../types'; const triggerDownload = (blob: Blob, filename: string) => { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; export const downloadJson = (data: object, filename: string) => { const jsonString = JSON.stringify(data, null, 2); const blob = new Blob([jsonString], { type: 'application/json' }); triggerDownload(blob, filename); }; export const downloadCsv = (dataset: Dataset, filename:string) => { if (!dataset.fullComparison) { console.error("No full comparison data to download."); alert("Full comparison data is not available for this dataset."); return; } const { fullComparison, data } = dataset; const headers = [ 'id', 'image_to_mesh_image', 'image_to_mesh_mesh', 'mesh_to_image_mesh', 'mesh_to_image_image', 'text_to_mesh_text', 'text_to_mesh_mesh', 'mesh_to_text_mesh', 'mesh_to_text_text', 'image_to_text_image', 'image_to_text_text', 'text_to_image_text', 'text_to_image_image', ]; let csvContent = headers.join(',') + '\n'; const imageMap = new Map(fullComparison.images.map(item => [item.source, item.matches])); const textMap = new Map(fullComparison.texts.map(item => [item.source, item.matches])); const meshMap = new Map(fullComparison.meshes.map(item => [item.source, item.matches])); const maxItems = Math.max( data.images.length, data.texts.length, data.meshes.length ); const formatMatches = (matches: { item: string; confidence: number }[] | undefined): string => { if (!matches || matches.length === 0) return ''; const items = matches.map(m => `'${m.item}'`); return `[${items.join(', ')}]`; }; for (let i = 0; i < maxItems; i++) { const row: Record = {}; headers.forEach(h => row[h] = ''); // Initialize with empty strings row.id = i; // Image to X const imageItem = data.images[i]; if (imageItem) { const matches = imageMap.get(imageItem.name); if (matches) { if (matches.mesh && matches.mesh.length > 0) { row.image_to_mesh_image = imageItem.name; row.image_to_mesh_mesh = formatMatches(matches.mesh); } if (matches.text && matches.text.length > 0) { row.image_to_text_image = imageItem.name; row.image_to_text_text = formatMatches(matches.text); } } } // Mesh to X const meshItem = data.meshes[i]; if (meshItem) { const matches = meshMap.get(meshItem.name); if (matches) { if (matches.image && matches.image.length > 0) { row.mesh_to_image_mesh = meshItem.name; row.mesh_to_image_image = formatMatches(matches.image); } if (matches.text && matches.text.length > 0) { row.mesh_to_text_mesh = meshItem.name; row.mesh_to_text_text = formatMatches(matches.text); } } } // Text to X const textItem = data.texts[i]; if (textItem) { const matches = textMap.get(textItem.name); if (matches) { if (matches.mesh && matches.mesh.length > 0) { row.text_to_mesh_text = textItem.name; row.text_to_mesh_mesh = formatMatches(matches.mesh); } if (matches.image && matches.image.length > 0) { row.text_to_image_text = textItem.name; row.text_to_image_image = formatMatches(matches.image); } } } const rowValues = headers.map(h => { const value = String(row[h]); // Quote the value if it contains a comma or a quote return value.includes(',') || value.includes('"') ? `"${value.replace(/"/g, '""')}"` : value; }); csvContent += rowValues.join(',') + '\n'; } const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); triggerDownload(blob, filename); };