/** * DFWDS-equivalent in Node — moves test datasheets from staging to For_Web, * decodes any DOS A-J prefix on filenames, quarantines invalid files. * * Mirrors the validation logic from the original VB6 DFWDS.bas: * - filename must be .txt * - must contain a dash * - work-order portion (left of dash) must be all-numeric and not start with 0 * - OR the first char is A-J (DOS-encoded), which decodes to 10-19 and renames * * Defaults match the original DFWDS_NAMES.txt: * --in \\ad2\webshare\Test_Datasheets * --bad \\ad2\webshare\Bad_Datasheets * --out \\ad2\webshare\For_Web * --log \\ad2\webshare\Datasheets_Log * * Use --dry-run to see what would happen without moving anything. */ const fs = require('fs'); const path = require('path'); const args = process.argv.slice(2); const arg = (n, d) => { const i = args.indexOf(n); return i >= 0 ? args[i+1] : d; }; const flag = (n) => args.includes(n); const IN_DIR = arg('--in', String.raw`C:\Shares\webshare\Test_Datasheets`); const BAD_DIR = arg('--bad', String.raw`C:\Shares\webshare\Bad_Datasheets`); const OUT_DIR = arg('--out', String.raw`C:\Shares\webshare\For_Web`); const LOG_DIR = arg('--log', String.raw`C:\Shares\webshare\Datasheets_Log`); const DRY = flag('--dry-run'); const LIMIT = parseInt(arg('--limit', '0'), 10); // Per VB funcDecodeWOchar: A-J map to 10..19 (one char -> two digits) const PREFIX_DECODE = { A: '10', B: '11', C: '12', D: '13', E: '14', F: '15', G: '16', H: '17', I: '18', J: '19', }; function classify(filename) { // Returns: {action: 'valid'|'rename'|'bad', newName?: string, reason?: string} if (!filename.toLowerCase().endsWith('.txt')) { return {action:'bad', reason:'not .txt'}; } const stem = filename.slice(0, -4); // strip .txt const dash = stem.indexOf('-'); if (dash <= 0) return {action:'bad', reason:'no dash or dash at start'}; const wo = stem.slice(0, dash).toUpperCase(); const tail = stem.slice(dash); // includes leading '-' if (/^\d+$/.test(wo)) { if (wo.startsWith('0')) return {action:'bad', reason:'WO starts with 0'}; return {action:'valid'}; } // Check DOS-encoded: first char A-J, rest all numeric const first = wo[0]; if (PREFIX_DECODE[first] && /^\d+$/.test(wo.slice(1))) { const newWo = PREFIX_DECODE[first] + wo.slice(1); return {action:'rename', newName: newWo + tail + '.txt'}; } return {action:'bad', reason:`WO "${wo}" not numeric and not A-J prefixed`}; } function ensureDir(d) { fs.mkdirSync(d, {recursive: true}); } function moveFile(src, dst) { ensureDir(path.dirname(dst)); if (fs.existsSync(dst)) { // overwrite by removing first; rename in same dir is fine but cross-dir on Win sometimes errors if exists fs.unlinkSync(dst); } fs.renameSync(src, dst); } function main() { if (!fs.existsSync(IN_DIR)) { console.error(`[FAIL] input dir not found: ${IN_DIR}`); process.exit(1); } ensureDir(BAD_DIR); ensureDir(OUT_DIR); ensureDir(LOG_DIR); // Log filename matches DFWDS pattern: DFWDS_YYYY_MM_DD.log const now = new Date(); const ymd = `${now.getFullYear()}_${String(now.getMonth()+1).padStart(2,'0')}_${String(now.getDate()).padStart(2,'0')}`; const logPath = path.join(LOG_DIR, `DFWDS_${ymd}.log`); const logFh = fs.openSync(logPath, 'a'); function log(msg) { const line = `[${new Date().toISOString()}] ${msg}\n`; process.stdout.write(line); fs.writeSync(logFh, line); } log(`=== DFWDS-process start (Node port) ===`); log(` in: ${IN_DIR}`); log(` out: ${OUT_DIR}`); log(` bad: ${BAD_DIR}`); log(` dry: ${DRY}, limit: ${LIMIT||'all'}`); const all = fs.readdirSync(IN_DIR).filter(n => fs.statSync(path.join(IN_DIR, n)).isFile()); const queue = LIMIT ? all.slice(0, LIMIT) : all; log(` queued: ${queue.length} files (of ${all.length} in dir)`); const stats = {valid:0, renamed:0, bad:0, errors:0}; for (const name of queue) { const src = path.join(IN_DIR, name); const cls = classify(name); try { if (cls.action === 'valid') { const dst = path.join(OUT_DIR, name); if (DRY) log(` [DRY VALID] ${name} -> ${dst}`); else { moveFile(src, dst); log(` VALID ${name}`); } stats.valid++; } else if (cls.action === 'rename') { const dst = path.join(OUT_DIR, cls.newName); if (DRY) log(` [DRY RENAME] ${name} -> ${cls.newName} -> ${dst}`); else { moveFile(src, dst); log(` RENAMED ${name} -> ${cls.newName}`); } stats.renamed++; } else { const dst = path.join(BAD_DIR, name); if (DRY) log(` [DRY BAD] ${name} (${cls.reason}) -> ${dst}`); else { moveFile(src, dst); log(` BAD ${name} (${cls.reason})`); } stats.bad++; } } catch (e) { log(` ERR ${name}: ${e.message}`); stats.errors++; } } log(`=== summary: valid=${stats.valid} renamed=${stats.renamed} bad=${stats.bad} errors=${stats.errors}`); fs.closeSync(logFh); console.log(`\n[INFO] log: ${logPath}`); } main();