refactor: usa decisione di destinazione unificata per folder e zip
This commit is contained in:
46
services/destinationIndex.js
Normal file
46
services/destinationIndex.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const CODE_FOLDER_REGEX = /^\d{3}$/;
|
||||||
|
const MAX_SCAN_DEPTH = 6;
|
||||||
|
|
||||||
|
async function buildDestinationIndex(destinationRoot) {
|
||||||
|
const index = new Map();
|
||||||
|
|
||||||
|
if (!destinationRoot || !(await fs.pathExists(destinationRoot))) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function walk(currentDir, depth) {
|
||||||
|
if (depth > MAX_SCAN_DEPTH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entries;
|
||||||
|
try {
|
||||||
|
entries = await fs.readdir(currentDir, { withFileTypes: true });
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullPath = path.join(currentDir, entry.name);
|
||||||
|
if (CODE_FOLDER_REGEX.test(entry.name)) {
|
||||||
|
const rows = index.get(entry.name) || [];
|
||||||
|
rows.push(fullPath);
|
||||||
|
index.set(entry.name, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
await walk(fullPath, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await walk(destinationRoot, 0);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { buildDestinationIndex };
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { findDestination, isCadFile } = require('./router');
|
const { getDestinationDecision, isCadFile } = require('./router');
|
||||||
|
const { buildDestinationIndex } = require('./destinationIndex');
|
||||||
|
|
||||||
async function processFolder(folder, config) {
|
async function processFolder(folder, config) {
|
||||||
const entries = await fs.readdir(folder, { withFileTypes: true });
|
const entries = await fs.readdir(folder, { withFileTypes: true });
|
||||||
|
const destinationIndex = await buildDestinationIndex(config?.destination);
|
||||||
const result = {
|
const result = {
|
||||||
scanned: 0,
|
scanned: 0,
|
||||||
copied: 0,
|
copied: 0,
|
||||||
@@ -25,15 +27,15 @@ async function processFolder(folder, config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const src = path.join(folder, file);
|
const src = path.join(folder, file);
|
||||||
const destDir = findDestination(file, config);
|
const decision = getDestinationDecision(file, config, destinationIndex);
|
||||||
|
const destDir = decision.destination;
|
||||||
|
|
||||||
if (!destDir) {
|
if (!destDir) {
|
||||||
result.skipped += 1;
|
result.skipped += 1;
|
||||||
result.details.push({ file, reason: 'Nessuna regola trovata' });
|
result.details.push({ file, reason: decision.reason || 'Nessuna regola trovata' });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.ensureDir(destDir);
|
|
||||||
const dest = path.join(destDir, file);
|
const dest = path.join(destDir, file);
|
||||||
await fs.copy(src, dest, { overwrite: true });
|
await fs.copy(src, dest, { overwrite: true });
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,72 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
function getCadType(filename) {
|
function normalizeCadType(ext) {
|
||||||
const baseName = path.basename(filename).toLowerCase();
|
const lowerExt = String(ext || '').toLowerCase();
|
||||||
const match = baseName.match(/\.(prt|asm|drw|dwr)(?:\.\d+)?$/);
|
return lowerExt === 'drw' ? 'dwr' : lowerExt;
|
||||||
return match ? match[1] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findDestination(filename, config) {
|
|
||||||
const cadType = getCadType(filename);
|
|
||||||
const globalDestination = typeof config?.destination === 'string' ? config.destination.trim() : '';
|
|
||||||
|
|
||||||
if (globalDestination && cadType) {
|
|
||||||
return globalDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const rule of config.rules || []) {
|
|
||||||
if ((rule.ext || '').toLowerCase() !== cadType) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rule.pattern) {
|
|
||||||
return rule.destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
const regex = new RegExp(rule.pattern);
|
|
||||||
if (regex.test(path.basename(filename))) {
|
|
||||||
return rule.destination;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCadInfo(filename) {
|
||||||
|
const baseName = path.basename(filename);
|
||||||
|
const match = baseName.match(/^(.*)\.(prt|asm|drw|dwr)(?:\.([^.]+))?$/i);
|
||||||
|
if (!match) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCadFile(filename) {
|
const rawCode = match[1];
|
||||||
return Boolean(getCadType(filename));
|
const type = normalizeCadType(match[2]);
|
||||||
|
const version = match[3] || '';
|
||||||
|
const digits = rawCode.replace(/\D/g, '');
|
||||||
|
const routingGroup = digits.length >= 5 ? digits.slice(2, 5) : null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: rawCode,
|
||||||
|
digits,
|
||||||
|
type,
|
||||||
|
version,
|
||||||
|
key: `${rawCode}.${type}`,
|
||||||
|
routingGroup,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { findDestination, isCadFile };
|
function getDestinationDecision(filename, config, destinationIndex) {
|
||||||
|
const cadInfo = getCadInfo(filename);
|
||||||
|
if (!cadInfo) {
|
||||||
|
return { destination: null, reason: 'Nessuna regola trovata' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cadInfo.routingGroup) {
|
||||||
|
return {
|
||||||
|
destination: null,
|
||||||
|
reason: 'Nome file non conforme: servono almeno 5 cifre nel nome per ricavare la 3a-4a-5a cifra',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = cadInfo.routingGroup;
|
||||||
|
const candidates = destinationIndex?.get(group) || [];
|
||||||
|
|
||||||
|
if (!candidates.length) {
|
||||||
|
return {
|
||||||
|
destination: null,
|
||||||
|
reason: `Sottocartella ${group} non trovata nella destinazione`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.length > 1) {
|
||||||
|
return {
|
||||||
|
destination: null,
|
||||||
|
reason: `Sottocartella ${group} trovata in ${candidates.length} percorsi diversi`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { destination: candidates[0], reason: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
function findDestination(filename, config, destinationIndex) {
|
||||||
|
return getDestinationDecision(filename, config, destinationIndex).destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCadFile(filename) {
|
||||||
|
return Boolean(getCadInfo(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { findDestination, getDestinationDecision, isCadFile, getCadInfo };
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ const unzipper = require('unzipper');
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { pipeline } = require('stream/promises');
|
const { pipeline } = require('stream/promises');
|
||||||
const { findDestination, isCadFile } = require('./router');
|
const { getDestinationDecision, isCadFile } = require('./router');
|
||||||
|
const { buildDestinationIndex } = require('./destinationIndex');
|
||||||
|
|
||||||
async function processZip(zipPath, config) {
|
async function processZip(zipPath, config) {
|
||||||
const stream = fs.createReadStream(zipPath).pipe(unzipper.Parse({ forceStream: true }));
|
const stream = fs.createReadStream(zipPath).pipe(unzipper.Parse({ forceStream: true }));
|
||||||
|
const destinationIndex = await buildDestinationIndex(config?.destination);
|
||||||
const result = {
|
const result = {
|
||||||
scanned: 0,
|
scanned: 0,
|
||||||
copied: 0,
|
copied: 0,
|
||||||
@@ -29,16 +31,16 @@ async function processZip(zipPath, config) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const destDir = findDestination(baseName, config);
|
const decision = getDestinationDecision(baseName, config, destinationIndex);
|
||||||
|
const destDir = decision.destination;
|
||||||
|
|
||||||
if (!destDir) {
|
if (!destDir) {
|
||||||
result.skipped += 1;
|
result.skipped += 1;
|
||||||
result.details.push({ file: baseName, reason: 'Nessuna regola trovata' });
|
result.details.push({ file: baseName, reason: decision.reason || 'Nessuna regola trovata' });
|
||||||
entry.autodrain();
|
entry.autodrain();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.ensureDir(destDir);
|
|
||||||
const dest = path.join(destDir, baseName);
|
const dest = path.join(destDir, baseName);
|
||||||
await pipeline(entry, fs.createWriteStream(dest));
|
await pipeline(entry, fs.createWriteStream(dest));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user