import type { UnifiedResultProjection, UnifiedResultQuery } from '../domain/unified-result'; import { buildUnifiedResultJsonl } from './unified-result-jsonl'; import { rowsToSpreadsheetSafeCsv } from './csv-export'; export function unifiedResultCsvFileName(generatedAt: string): string { return `assessor-result-db-filtered-${generatedAt.slice(0, 10)}.csv`; } /** Use the same disclosed projection as JSONL; nested values remain complete JSON text. */ export function buildUnifiedResultCsv(query: UnifiedResultQuery, results: UnifiedResultProjection[], generatedAt: string): string { const [manifest, ...entries] = buildUnifiedResultJsonl(query, results, generatedAt).trimEnd().split('\n') .map(line => JSON.parse(line) as Record); const records = entries.map(entry => entry.result as Record); const preferred = ['title', 'sourceRuleId', 'sourceTool', 'severityNormalized', 'outcomeNormalized', 'resultType', 'isIoe', 'isDeviation', 'description', 'recommendation']; const fields = [...new Set(records.flatMap(record => Object.keys(record)))]; const rank = (field: string) => preferred.includes(field) ? preferred.indexOf(field) : preferred.length; fields.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b)); const handling = manifest.handling as { classification: string; notice: string }; const metadata = [manifest.generatedOn, manifest.disclosurePolicyVersion, handling.classification, handling.notice]; const headers = ['Position', ...fields, 'exportGeneratedOn', 'exportDisclosurePolicyVersion', 'exportClassification', 'exportNotice']; const value = (cell: unknown) => cell !== null && typeof cell === 'object' ? JSON.stringify(cell) : cell; const rows = [headers, ...records.map((record, index) => [index + 1, ...fields.map(field => value(record[field])), ...metadata])]; return '\uFEFF' + rowsToSpreadsheetSafeCsv(rows, ';') + '\n'; }