#!/usr/bin/env node /** * Retime all chart JSON files under mocks/device-cgi-simulator/chartsData (recursively). * For any JSON array where elements contain a 't' timestamp string in format 'YYYY-MM-DD HH:mm:ss', * rebase timestamps so the first element's time-of-day is preserved but moved to today's date, * and all other elements keep their original relative deltas. * * Usage: * node ./mocks/scripts/retimeAllCharts.mjs [baseDir] * Default baseDir: mocks/device-cgi-simulator/chartsData */ import fs from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const workspaceRoot = path.resolve(__dirname, "../.."); function pad(n) { return String(n).padStart(2, "0"); } function formatDate(d) { const yyyy = d.getFullYear(); const MM = pad(d.getMonth() + 1); const dd = pad(d.getDate()); const hh = pad(d.getHours()); const mm = pad(d.getMinutes()); const ss = pad(d.getSeconds()); return `${yyyy}-${MM}-${dd} ${hh}:${mm}:${ss}`; } function parseDateTime(str) { const m = typeof str === "string" && str.match(/(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})/); if (!m) return null; const [, y, mo, d, h, mi, s] = m.map(Number); return new Date(y, mo - 1, d, h, mi, s, 0); } function withTodayDateAndTimeOf(baseTime) { const today = new Date(); return new Date( today.getFullYear(), today.getMonth(), today.getDate(), baseTime.getHours(), baseTime.getMinutes(), baseTime.getSeconds(), 0 ); } async function readJson(filePath) { const raw = await fs.readFile(filePath, "utf-8"); return JSON.parse(raw); } async function writeJson(filePath, data) { const content = JSON.stringify(data, null, 2); await fs.writeFile(filePath, content + "\n", "utf-8"); } async function retimeArrayWithT(arr) { // Find first element with parsable 't' let idx0 = -1; let t0 = null; for (let i = 0; i < arr.length; i++) { const t = arr[i]?.t; const d = parseDateTime(t); if (d) { idx0 = i; t0 = d; break; } } if (idx0 === -1) return null; // nothing to retime const newBase = withTodayDateAndTimeOf(t0); const t0ms = t0.getTime(); const updated = arr.map((item) => { if (!item || typeof item !== "object") return item; if (!("t" in item)) return item; const d = parseDateTime(item.t); if (!d) return item; const delta = t0ms - d.getTime(); const newDate = new Date(newBase.getTime() - delta); return { ...item, t: formatDate(newDate) }; }); return updated; } async function processJsonFile(filePath) { try { const data = await readJson(filePath); if (!Array.isArray(data) || data.length === 0) { return { status: "skip", reason: "not an array or empty" }; } const updated = await retimeArrayWithT(data); if (!updated) { return { status: "skip", reason: "no parsable t fields" }; } await writeJson(filePath, updated); return { status: "ok" }; } catch (e) { return { status: "error", message: e.message }; } } async function walk(dir, visitor) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const p = path.join(dir, entry.name); if (entry.isDirectory()) { await walk(p, visitor); } else if (entry.isFile() && p.toLowerCase().endsWith(".json")) { await visitor(p); } } } async function main() { const baseArg = process.argv[2]; const baseDir = path.resolve( workspaceRoot, baseArg || "mocks/device-cgi-simulator/chartsData" ); let ok = 0, skipped = 0, errors = 0; const rel = (p) => path.relative(workspaceRoot, p) || p; try { await walk(baseDir, async (file) => { const res = await processJsonFile(file); if (res.status === "ok") { ok++; console.log(`[ok] Updated ${rel(file)}`); } else if (res.status === "skip") { skipped++; console.log(`[skip] ${rel(file)}: ${res.reason}`); } else { errors++; console.log(`[error] ${rel(file)}: ${res.message}`); } }); } catch (err) { console.error("Failed to retime charts:", err); process.exitCode = 1; return; } console.log(`\nDone. ok=${ok}, skipped=${skipped}, errors=${errors}`); } await main();