function show(el) { el.classList.remove("hidden"); } function hide(el) { el.classList.add("hidden"); } function escapeHtml(str) { return String(str) .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function splitCsvLine(line) { const out = []; let cur = ""; let inQuotes = false; for (let i = 0; i < line.length; i++) { const ch = line[i]; if (ch === '"') { if (inQuotes && line[i + 1] === '"') { cur += '"'; i++; } else { inQuotes = !inQuotes; } } else if (ch === "," && !inQuotes) { out.push(cur); cur = ""; } else { cur += ch; } } out.push(cur); return out.map(v => v.trim()); } const fileInput = document.getElementById("file_input"); const step2 = document.getElementById("step-2"); const step3 = document.getElementById("step-3"); const step4 = document.getElementById("step-4"); const step5 = document.getElementById("step-5"); const labelSelect = document.getElementById("label_select"); const labelError = document.getElementById("label_error"); const normalBox = document.getElementById("normal_values"); const normalError = document.getElementById("normal_error"); const featureList = document.getElementById("feature_list"); const featureCounter = document.getElementById("feature_counter"); const featureError = document.getElementById("feature_error"); const btnStep2Next = document.getElementById("btn_step2_next"); const btnStep3Next = document.getElementById("btn_step3_next"); const btnRun = document.getElementById("btn_run"); const MAX_FEATURES = 5; let headers = []; let rows = []; let labelIndex = -1; let numericColumnIdx = []; fileInput.addEventListener("change", async () => { const formData = new FormData(); formData.append("file", fileInput.files[0]); await fetch("/upload-dataset", { method: "POST", body: formData }); if (!fileInput.files.length) return; const file = fileInput.files[0]; const text = await file.text(); const lines = text.split(/\r?\n/).filter(l => l.trim() !== ""); if (lines.length < 2) return; headers = splitCsvLine(lines[0]); rows = lines.slice(1).map(splitCsvLine); labelSelect.innerHTML = ``; headers.forEach((h, idx) => { const opt = document.createElement("option"); opt.value = idx; opt.textContent = h; labelSelect.appendChild(opt); }); numericColumnIdx = detectNumericColumns(headers, rows, 30); show(step2); hide(step3); hide(step4); hide(step5); hide(labelError); }); function detectNumericColumns(headers, rows, sampleCount) { const result = []; const samples = Math.min(sampleCount, rows.length); for (let c = 0; c < headers.length; c++) { let valid = 0; let total = 0; for (let r = 0; r < samples; r++) { const v = rows[r][c]; if (v === undefined || v === "") continue; total++; if (!isNaN(Number(v))) valid++; } if (total > 0 && valid / total >= 0.9) { result.push(c); } } return result; } btnStep2Next.addEventListener("click", () => { if (labelSelect.value === "") { show(labelError); return; } hide(labelError); labelIndex = Number(labelSelect.value); const uniques = new Set(); rows.forEach(r => { const v = r[labelIndex]; if (v !== undefined && v !== "") uniques.add(v.trim()); }); normalBox.innerHTML = ""; uniques.forEach(val => { const label = document.createElement("label"); label.className = "flex items-center gap-3 bg-gray-900/40 border border-gray-700 rounded-lg px-4 py-2"; label.innerHTML = ` ${escapeHtml(val)} `; normalBox.appendChild(label); }); normalBox.querySelectorAll("input").forEach(cb => { cb.addEventListener("change", () => { if (cb.checked) { normalBox.querySelectorAll("input").forEach(o => { if (o !== cb) o.checked = false; }); } }); }); show(step3); hide(step4); hide(step5); hide(normalError); }); btnStep3Next.addEventListener("click", () => { const checked = normalBox.querySelectorAll("input:checked"); if (checked.length !== 1) { normalError.textContent = "Select exactly one NORMAL value."; show(normalError); return; } hide(normalError); featureList.innerHTML = ""; numericColumnIdx .filter(idx => idx !== labelIndex) .forEach(idx => { const label = document.createElement("label"); label.className = "flex items-center justify-between bg-gray-900/40 border border-gray-700 rounded-lg px-4 py-2"; label.innerHTML = `