import { computeLedger } from "./ledger.js?v=__ASSET_VERSION__"; import { buildPlanCurve } from "./curve.js?v=__ASSET_VERSION__"; import { parseDuration } from "./time.js?v=__ASSET_VERSION__"; export const MILESTONES = [ ["charge", "Charge"], ["tp", "Turning point"], ["yellow", "Yellow / dry end"], ["fc", "First crack"], ["drop", "Drop"], ]; export function sanitizeObservations(raw) { if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null; const out = {}; for (const [key] of MILESTONES) { if (raw[key] === undefined) continue; const row = raw[key]; if (!row || typeof row !== "object" || Array.isArray(row)) return null; const clean = {}; for (const field of ["timeS", "tempC"]) { const v = row[field]; if (v === undefined || v === null) { clean[field] = null; continue; } if ( typeof v !== "number" || !Number.isFinite(v) || v < 0 || v > (field === "timeS" ? 7200 : 350) ) return null; clean[field] = v; } clean.note = String(row.note ?? "").slice(0, 2000); out[key] = clean; } return out; } export function buildComparison(detail) { const points = detail.plan ? buildPlanCurve(detail.plan, computeLedger(detail.plan)) : []; return MILESTONES.map(([key, label]) => { const target = points.find((p) => p.key === key); const uploaded = key === "tp" ? detail.parsed?.turningPoint : detail.parsed?.milestones?.find((p) => p.key === key); const saved = detail.observations?.[key], legacy = detail.plan?.planActual?.[key]; const timeS = saved?.timeS ?? uploaded?.timeS ?? parseDuration(legacy?.actualTime); const legacyTemp = legacy?.actualBt !== "" && legacy?.actualBt != null ? Number(legacy.actualBt) : null; const tempC = saved?.tempC ?? uploaded?.tempC ?? (Number.isFinite(legacyTemp) ? legacyTemp : null); return { key, label, target, timeS, tempC, note: saved?.note ?? legacy?.note ?? "", deltaS: target && timeS != null ? timeS - target.timeS : null, }; }); }