import Bun from 'bun'; import path from 'node:path'; import { parseArgs } from 'node:util'; import fs from 'node:fs'; const { values } = parseArgs({ args: Bun.argv, options: { workingDir: { type: "string" }, dataDir: { type: "string" }, script: { type: "string" }, }, strict: true, allowPositionals: true, }); if (!values.workingDir || !values.dataDir || !values.script) { console.error("Usage: bun run script/compare.ts --workingDir --dataDir --script "); process.exit(1); } const workingDir = values.workingDir; const dataDir = values.dataDir; const predictScript = values.script; const tempFileName = "temp.json"; const tempFilePath = path.join(workingDir, tempFileName); // 1. 전처리 실행 (temp.json 생성하여 기존 features.json 보존) console.log("Step 1: Running preprocessing to temp.json..."); const preprocessResult = Bun.spawnSync([ "bun", "run", "script/preprocess.ts", "--workingDir", workingDir, "--dataDir", dataDir, "--fileName", tempFileName ]); if (!preprocessResult.success) { console.error("Preprocessing failed"); console.error(preprocessResult.stderr.toString()); process.exit(1); } // 2. measure.csv 로드 console.log("Step 2: Loading measure.csv..."); const measurePath = path.join(dataDir, "measure.csv"); const measureContent = fs.readFileSync(measurePath, "utf-8"); const measureMap = new Map(); measureContent.split("\n").forEach((line, index) => { if (index === 0 || !line.trim()) return; const parts = line.split(","); if (parts.length >= 3) { const constant = parts[0]; const songno = parts[1]; const diff = parts[2]; measureMap.set(`${songno.trim()}_${diff.trim()}`, parseFloat(constant)); } }); // 3. temp.json 로드하여 대상 곡 목록 추출 const features = JSON.parse(fs.readFileSync(tempFilePath, "utf-8")); const uniqueSongnos = Array.from(new Set(features.map((f: any) => f.songno))); // 4. 예측 및 비교 console.log(`Step 3: Predicting and comparing ${uniqueSongnos.length} songs...`); const comparisonResults: any[] = []; let processedCount = 0; for (const songno of uniqueSongnos) { try { const predictProcess = Bun.spawnSync([ "python3", predictScript, "--workingDir", workingDir, "--songno", songno as string, "--feature", tempFilePath ]); if (!predictProcess.success) { console.error(`\n[ERROR] Failed to predict songno ${songno}`); console.error(predictProcess.stderr.toString()); processedCount++; continue; } const output = predictProcess.stdout.toString().trim(); // JSON 부분만 추출 (경고문 등이 섞여있을 경우 대비) const jsonStart = output.indexOf('['); const jsonEnd = output.lastIndexOf(']') + 1; if (jsonStart === -1 || jsonEnd === 0) { console.error(`\n[ERROR] Invalid output format for songno ${songno}`); processedCount++; continue; } const predictions = JSON.parse(output.substring(jsonStart, jsonEnd)); predictions.forEach((pred: any) => { const key = `${pred.songno}_${pred.diff}`; const actual = measureMap.get(key); if (actual !== undefined) { comparisonResults.push({ songno: pred.songno, diff: pred.diff, actual: actual, predicted: pred.predicted, error: Math.abs(actual - pred.predicted) }); } }); processedCount++; if (processedCount % 10 === 0 || processedCount === uniqueSongnos.length) { const percent = ((processedCount / uniqueSongnos.length) * 100).toFixed(1); process.stdout.write(`\rProgress: ${processedCount}/${uniqueSongnos.length} (${percent}%) `); } } catch (err) { console.error(`\nError processing songno ${songno}:`, err); processedCount++; } } console.log("\nPrediction finished."); // 5. 결과 분석 및 저장 if (comparisonResults.length === 0) { console.error("No comparison results were generated."); process.exit(1); } const avgError = comparisonResults.reduce((acc, curr) => acc + curr.error, 0) / comparisonResults.length; const resultData = { summary: { total_compared: comparisonResults.length, average_absolute_error: avgError, timestamp: new Date().toISOString(), script_used: predictScript }, details: comparisonResults.sort((a, b) => b.error - a.error) }; const comparePath = path.join(workingDir, "compare.json"); fs.writeFileSync(comparePath, JSON.stringify(resultData, null, 2), "utf-8"); console.log(`\nComparison complete!`); console.log(`Total compared: ${comparisonResults.length}`); console.log(`Average Error: ${avgError.toFixed(4)}`); console.log(`Results saved to: ${comparePath}`);