feat(processing): smistamento ricorsivo cartelle + gestione unrouted per ZIP e folder
This commit is contained in:
@@ -33,6 +33,7 @@ function renderResult(title, result) {
|
|||||||
`scansionati: ${result.scanned ?? 0}`,
|
`scansionati: ${result.scanned ?? 0}`,
|
||||||
`copiati: ${result.copied ?? 0}`,
|
`copiati: ${result.copied ?? 0}`,
|
||||||
`saltati: ${result.skipped ?? 0}`,
|
`saltati: ${result.skipped ?? 0}`,
|
||||||
|
`non smistati: ${result.unrouted ?? 0}`,
|
||||||
'',
|
'',
|
||||||
'dettagli (max 20):',
|
'dettagli (max 20):',
|
||||||
detailsText,
|
detailsText,
|
||||||
|
|||||||
@@ -2,23 +2,45 @@ const fs = require('fs-extra');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { getDestinationDecision, isCadFile } = require('./router');
|
const { getDestinationDecision, isCadFile } = require('./router');
|
||||||
const { buildDestinationIndex } = require('./destinationIndex');
|
const { buildDestinationIndex } = require('./destinationIndex');
|
||||||
|
const { getUnroutedTarget } = require('./unrouted');
|
||||||
|
|
||||||
|
async function collectFilesRecursively(rootDir) {
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
async function walk(currentDir) {
|
||||||
|
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = path.join(currentDir, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
await walk(fullPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.isFile()) {
|
||||||
|
files.push({ fullPath, fileName: entry.name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await walk(rootDir);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
async function processFolder(folder, config) {
|
async function processFolder(folder, config) {
|
||||||
const entries = await fs.readdir(folder, { withFileTypes: true });
|
const files = await collectFilesRecursively(folder);
|
||||||
const destinationIndex = await buildDestinationIndex(config?.destination);
|
const destinationIndex = await buildDestinationIndex(config?.destination);
|
||||||
const result = {
|
const result = {
|
||||||
scanned: 0,
|
scanned: 0,
|
||||||
copied: 0,
|
copied: 0,
|
||||||
skipped: 0,
|
skipped: 0,
|
||||||
|
unrouted: 0,
|
||||||
details: [],
|
details: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const { fullPath, fileName } of files) {
|
||||||
if (!entry.isFile()) {
|
const file = fileName;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const file = entry.name;
|
|
||||||
result.scanned += 1;
|
result.scanned += 1;
|
||||||
|
|
||||||
if (!isCadFile(file)) {
|
if (!isCadFile(file)) {
|
||||||
@@ -26,18 +48,25 @@ async function processFolder(folder, config) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const src = path.join(folder, file);
|
|
||||||
const decision = getDestinationDecision(file, config, destinationIndex);
|
const decision = getDestinationDecision(file, config, destinationIndex);
|
||||||
const destDir = decision.destination;
|
const destDir = decision.destination;
|
||||||
|
|
||||||
if (!destDir) {
|
if (!destDir) {
|
||||||
result.skipped += 1;
|
const unroutedTarget = await getUnroutedTarget(file);
|
||||||
result.details.push({ file, reason: decision.reason || 'Nessuna regola trovata' });
|
await fs.copy(fullPath, unroutedTarget.destinationPath, { overwrite: false });
|
||||||
|
|
||||||
|
result.copied += 1;
|
||||||
|
result.unrouted += 1;
|
||||||
|
result.details.push({
|
||||||
|
file,
|
||||||
|
destination: unroutedTarget.destinationDir,
|
||||||
|
reason: decision.reason || 'Nessuna regola trovata',
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dest = path.join(destDir, file);
|
const dest = path.join(destDir, file);
|
||||||
await fs.copy(src, dest, { overwrite: true });
|
await fs.copy(fullPath, dest, { overwrite: true });
|
||||||
|
|
||||||
result.copied += 1;
|
result.copied += 1;
|
||||||
result.details.push({ file, destination: destDir });
|
result.details.push({ file, destination: destDir });
|
||||||
|
|||||||
53
services/unrouted.js
Normal file
53
services/unrouted.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
const PRIMARY_UNROUTED_DIR = '/cadroute/__NON_SMISTATI';
|
||||||
|
const HOME_UNROUTED_DIR = path.join(os.homedir(), '.cadroute', '__NON_SMISTATI');
|
||||||
|
|
||||||
|
let resolvedUnroutedDirPromise = null;
|
||||||
|
|
||||||
|
async function resolveUnroutedDir() {
|
||||||
|
if (!resolvedUnroutedDirPromise) {
|
||||||
|
resolvedUnroutedDirPromise = (async () => {
|
||||||
|
try {
|
||||||
|
await fs.ensureDir(PRIMARY_UNROUTED_DIR);
|
||||||
|
return PRIMARY_UNROUTED_DIR;
|
||||||
|
} catch {
|
||||||
|
await fs.ensureDir(HOME_UNROUTED_DIR);
|
||||||
|
return HOME_UNROUTED_DIR;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedUnroutedDirPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUniquePath(destinationDir, fileName) {
|
||||||
|
const parsed = path.parse(fileName);
|
||||||
|
let candidate = path.join(destinationDir, fileName);
|
||||||
|
let counter = 1;
|
||||||
|
|
||||||
|
while (await fs.pathExists(candidate)) {
|
||||||
|
candidate = path.join(destinationDir, `${parsed.name}__${counter}${parsed.ext}`);
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUnroutedTarget(fileName) {
|
||||||
|
const destinationDir = await resolveUnroutedDir();
|
||||||
|
const destinationPath = await getUniquePath(destinationDir, fileName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
destinationDir,
|
||||||
|
destinationPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getUnroutedTarget,
|
||||||
|
PRIMARY_UNROUTED_DIR,
|
||||||
|
HOME_UNROUTED_DIR,
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ const path = require('path');
|
|||||||
const { pipeline } = require('stream/promises');
|
const { pipeline } = require('stream/promises');
|
||||||
const { getDestinationDecision, isCadFile } = require('./router');
|
const { getDestinationDecision, isCadFile } = require('./router');
|
||||||
const { buildDestinationIndex } = require('./destinationIndex');
|
const { buildDestinationIndex } = require('./destinationIndex');
|
||||||
|
const { getUnroutedTarget } = require('./unrouted');
|
||||||
|
|
||||||
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 }));
|
||||||
@@ -12,6 +13,7 @@ async function processZip(zipPath, config) {
|
|||||||
scanned: 0,
|
scanned: 0,
|
||||||
copied: 0,
|
copied: 0,
|
||||||
skipped: 0,
|
skipped: 0,
|
||||||
|
unrouted: 0,
|
||||||
details: [],
|
details: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,9 +37,16 @@ async function processZip(zipPath, config) {
|
|||||||
const destDir = decision.destination;
|
const destDir = decision.destination;
|
||||||
|
|
||||||
if (!destDir) {
|
if (!destDir) {
|
||||||
result.skipped += 1;
|
const unroutedTarget = await getUnroutedTarget(baseName);
|
||||||
result.details.push({ file: baseName, reason: decision.reason || 'Nessuna regola trovata' });
|
await pipeline(entry, fs.createWriteStream(unroutedTarget.destinationPath));
|
||||||
entry.autodrain();
|
|
||||||
|
result.copied += 1;
|
||||||
|
result.unrouted += 1;
|
||||||
|
result.details.push({
|
||||||
|
file: baseName,
|
||||||
|
destination: unroutedTarget.destinationDir,
|
||||||
|
reason: decision.reason || 'Nessuna regola trovata',
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user