176 lines
5.6 KiB
JavaScript
176 lines
5.6 KiB
JavaScript
const { consultarViabilidade, discoverDataType } = require('./viabilidadeService');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const readline = require('readline');
|
|
const { once } = require('events');
|
|
const {
|
|
createJob,
|
|
incrementProcessed,
|
|
incrementErrors,
|
|
finishJob,
|
|
failJob
|
|
} = require('./jobStore.service');
|
|
|
|
// conta linhas válidas no CSV (com CEP e Número ou Latitude e Longitude)
|
|
async function countValidLines(inputPath) {
|
|
const dataType = await discoverDataType(inputPath);
|
|
const instream = fs.createReadStream(inputPath, { encoding: 'utf8' });
|
|
const rl = readline.createInterface({ input: instream, crlfDelay: Infinity });
|
|
|
|
let isHeader = true;
|
|
let headers = [];
|
|
let idxCep = -1;
|
|
let idxNumero = -1;
|
|
let idxLatitude = -1;
|
|
let idxLongitude = -1;
|
|
let total = 0;
|
|
|
|
for await (const rawLine of rl) {
|
|
const line = rawLine.replace(/\r$/, '');
|
|
if (!line.trim()) continue;
|
|
|
|
if (isHeader) {
|
|
headers = line.split(';').map(h => h.trim());
|
|
const lower = headers.map(h => h.toLowerCase());
|
|
idxCep = lower.indexOf('cep');
|
|
idxNumero = lower.indexOf('numero');
|
|
idxLatitude = lower.indexOf('latitude');
|
|
idxLongitude = lower.indexOf('longitude');
|
|
isHeader = false;
|
|
continue;
|
|
}
|
|
|
|
const cols = line.split(';').map(c => c.trim());
|
|
if (dataType === 'cep') {
|
|
const cep = idxCep >= 0 ? String(cols[idxCep] || '').replace(/\D/g, '') : '';
|
|
const numero = idxNumero >= 0 ? cols[idxNumero] : '';
|
|
if (cep && numero) total++;
|
|
} else if (dataType === 'geolocalizacao') {
|
|
const latitude = idxLatitude >= 0 ? parseFloat(cols[idxLatitude]) : NaN;
|
|
const longitude = idxLongitude >= 0 ? parseFloat(cols[idxLongitude]) : NaN;
|
|
if (!isNaN(latitude) && !isNaN(longitude)) total++;
|
|
}
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
// nova função: processa CSV linha a linha, chama consultarViabilidade e gera CSV de saída
|
|
// Recebe jobId já criado no controller
|
|
async function processCsvFile(jobId, inputPath, originalName) {
|
|
const dataType = await discoverDataType(inputPath);
|
|
const total = await countValidLines(inputPath);
|
|
// Job já criado no controller
|
|
// const jobId = createJob(total);
|
|
const baseName = path.parse(inputPath).name;
|
|
const outputFilename = `processed_${Date.now()}_${baseName}.csv`;
|
|
const outputPath = path.join(__dirname, '..', 'outputs', outputFilename);
|
|
|
|
const instream = fs.createReadStream(inputPath, { encoding: 'utf8' });
|
|
const rl = readline.createInterface({ input: instream, crlfDelay: Infinity });
|
|
const outStream = fs.createWriteStream(outputPath, { encoding: 'utf8' });
|
|
outStream.write('\uFEFF');
|
|
|
|
let isHeader = true;
|
|
let headers = [];
|
|
let idxCep = -1;
|
|
let idxNumero = -1;
|
|
let idxLatitude = -1;
|
|
let idxLongitude = -1;
|
|
|
|
for await (const rawLine of rl) {
|
|
const line = rawLine.replace(/\r$/, ''); // normalize CRLF
|
|
if (!line.trim()) continue;
|
|
|
|
if (isHeader) {
|
|
headers = line
|
|
.split(';')
|
|
.map(h => h.trim())
|
|
.filter(h => h !== '');
|
|
|
|
const lower = headers.map(h => h.toLowerCase());
|
|
idxCep = lower.indexOf('cep');
|
|
idxNumero = lower.indexOf('numero');
|
|
idxLatitude = lower.indexOf('latitude');
|
|
idxLongitude = lower.indexOf('longitude');
|
|
|
|
// se não encontrar, tenta variações comuns
|
|
const idx = lower.indexOf('codigo postal');
|
|
if (idx !== -1) idxCep = idx;
|
|
|
|
|
|
const outHeaders = [...headers, 'Distancia', 'Endereco', 'Não Dedicado', 'Dedicado', 'Erro'];
|
|
outStream.write(outHeaders.join(';') + '\n');
|
|
isHeader = false;
|
|
continue;
|
|
}
|
|
|
|
const cols = line
|
|
.split(';')
|
|
.map(c => c.trim())
|
|
.filter(c => c !== '');
|
|
|
|
let dataToSend = {};
|
|
if (dataType === 'cep') {
|
|
const cepRaw = (idxCep >= 0 && cols[idxCep]) ? cols[idxCep] : '';
|
|
const cep = String(cepRaw).replace(/\D/g, ''); // keep digits only
|
|
const numero = (idxNumero >= 0 && cols[idxNumero]) ? cols[idxNumero] : '';
|
|
|
|
if (!cep || !numero) {
|
|
continue; // pula linha inválida
|
|
}
|
|
dataToSend = { cep, numero };
|
|
} else if (dataType === 'geolocalizacao') {
|
|
const latitude = (idxLatitude >= 0 && cols[idxLatitude]) ? parseFloat(cols[idxLatitude]) : NaN;
|
|
const longitude = (idxLongitude >= 0 && cols[idxLongitude]) ? parseFloat(cols[idxLongitude]) : NaN;
|
|
|
|
if (isNaN(latitude) || isNaN(longitude)) {
|
|
continue; // pula linha inválida
|
|
}
|
|
dataToSend = { latitude, longitude };
|
|
} else {
|
|
continue; // tipo desconhecido, pula
|
|
}
|
|
|
|
try {
|
|
const viab = await consultarViabilidade(dataToSend);
|
|
|
|
const distancia = viab.distancia ?? (viab.raw && (viab.raw.distancia || viab.raw.distance)) ?? '';
|
|
|
|
const endereco = viab.endereco;
|
|
|
|
if (viab.naoDedicado) {
|
|
var naoDedicado = "Viavel";
|
|
} else {
|
|
var naoDedicado = "Não Viavel";
|
|
}
|
|
|
|
if (viab.dedicado) {
|
|
var dedicado = "Viavel";
|
|
} else {
|
|
var dedicado = "Não Viavel";
|
|
}
|
|
|
|
const error = viab.error ? String(viab.error).replace(/[\r\n;]/g, ' ') : '';
|
|
|
|
const outCols = [...cols, distancia, endereco, naoDedicado, dedicado, error];
|
|
outStream.write(outCols.join(';') + '\n');
|
|
incrementProcessed(jobId);
|
|
} catch (err) {
|
|
const errMsg = (err && (err.message || String(err))).replace(/[\r\n;]/g, ' ');
|
|
const outCols = [...cols, '', '', '', '', '', '', errMsg];
|
|
outStream.write(outCols.join(';') + '\n');
|
|
incrementErrors(jobId);
|
|
incrementProcessed(jobId);
|
|
}
|
|
}
|
|
|
|
outStream.end();
|
|
await once(outStream, 'finish');
|
|
|
|
finishJob(jobId, path.basename(outputPath));
|
|
|
|
return outputPath;
|
|
}
|
|
|
|
module.exports = { processCsvFile, countValidLines }; |