|
|
@ -18,15 +18,6 @@ const { basedir } = require('../config/constants'); |
|
|
|
const dest = path.join(basedir, 'public/csv'); |
|
|
|
|
|
|
|
|
|
|
|
// Create a generateFilePath function witch returns a path with a filename and datetime
|
|
|
|
function generateFilePath(filename) { |
|
|
|
return { |
|
|
|
filepath: path.join(dest, `${filename}-${Date.now()}.csv`), |
|
|
|
generatedpath: path.join(dest, `${filename}-generated-${Date.now()}.csv`), |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Create a class File that extends EventEmitter
|
|
|
|
class File { |
|
|
|
|
|
|
@ -37,8 +28,30 @@ class File { |
|
|
|
constructor(url) { |
|
|
|
this.url = url; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
formatDateToCustomFormat(date) { |
|
|
|
// Obtenir les composantes de la date et de l'heure
|
|
|
|
const year = date.getFullYear(); |
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0'); // Janvier est 0, février est 1, etc.
|
|
|
|
const day = String(date.getDate()).padStart(2, '0'); |
|
|
|
const hours = String(date.getHours()).padStart(2, '0'); |
|
|
|
const minutes = String(date.getMinutes()).padStart(2, '0'); |
|
|
|
const seconds = String(date.getSeconds()).padStart(2, '0'); |
|
|
|
|
|
|
|
// Créer la chaîne au format personnalisé
|
|
|
|
const formattedDate = `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`; |
|
|
|
|
|
|
|
return formattedDate; |
|
|
|
} |
|
|
|
|
|
|
|
// Create a generateFilePath function witch returns a path with a filename and datetime
|
|
|
|
generateFilePath(filename) { |
|
|
|
const date = new Date(); |
|
|
|
return { |
|
|
|
filepath: path.join(dest, `${filename}-${this.formatDateToCustomFormat(date)}.csv`), |
|
|
|
generatedpath: path.join(dest, `${filename}-generated-${this.formatDateToCustomFormat(date)}.csv`), |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Download a file from a url |
|
|
@ -65,7 +78,7 @@ class File { |
|
|
|
async download() { |
|
|
|
const url = URL.parse(this.url); |
|
|
|
this.filename = slugify(url.hostname, { lower: true }); |
|
|
|
const { filepath, generatedpath } = generateFilePath(this.filename); |
|
|
|
const { filepath, generatedpath } = this.generateFilePath(this.filename); |
|
|
|
this.filepath = filepath; |
|
|
|
this.generatedpath = generatedpath; |
|
|
|
|
|
|
@ -139,74 +152,73 @@ class File { |
|
|
|
|
|
|
|
// create a parse method which read the file and return a stream
|
|
|
|
parse(columns) { |
|
|
|
const stream = new PassThrough(); |
|
|
|
const fileStream = fs.createWriteStream(this.generatedpath); |
|
|
|
|
|
|
|
// check if columns is valid
|
|
|
|
if (!columns || !columns.length) { |
|
|
|
// return Promise.reject(new Error('Invalid columns'));
|
|
|
|
emitter.emit('parse.error', { url: this.url, filepath: this.filepath, error: 'Invalid columns' }); |
|
|
|
return false; |
|
|
|
} |
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
const fileStream = fs.createWriteStream(this.generatedpath); |
|
|
|
|
|
|
|
// check if columns is valid
|
|
|
|
if (!columns || !columns.length) { |
|
|
|
// return Promise.reject(new Error('Invalid columns'));
|
|
|
|
emitter.emit('parse.error', { url: this.url, filepath: this.filepath, error: 'Invalid columns' }); |
|
|
|
reject(new Error('Invalid columns')); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
// Create a variable to hold csv columns indexes
|
|
|
|
const columnsIndex = {}; |
|
|
|
for (let column of columns) { |
|
|
|
columnsIndex[column] = { |
|
|
|
exist: false, |
|
|
|
main: (String(column)).split('.')[0], |
|
|
|
value: column, |
|
|
|
rest: (String(column)).split('.').slice(1).join('.'), |
|
|
|
last: (String(column)).split('.').pop(), |
|
|
|
}; |
|
|
|
} |
|
|
|
// Create a variable to hold csv columns indexes
|
|
|
|
const columnsIndex = {}; |
|
|
|
for (let column of columns) { |
|
|
|
columnsIndex[column] = { |
|
|
|
exist: false, |
|
|
|
main: (String(column)).split('.')[0], |
|
|
|
value: column, |
|
|
|
rest: (String(column)).split('.').slice(1).join('.'), |
|
|
|
last: (String(column)).split('.').pop(), |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const columnsFiltered = []; |
|
|
|
|
|
|
|
let count = 1; |
|
|
|
fs.createReadStream(this.filepath) |
|
|
|
.pipe(csvParser({ separator: ';' })) |
|
|
|
.on('headers', (headers) => { |
|
|
|
headers = headers.map(header => typeof header === 'string' ? header.trim() : header); |
|
|
|
|
|
|
|
const result = []; |
|
|
|
for (let key in columnsIndex) { |
|
|
|
columnsIndex[key].exist = headers.includes(columnsIndex[key].main); |
|
|
|
if ( columnsIndex[key].exist ) { |
|
|
|
columnsFiltered.push(columnsIndex[key].value); |
|
|
|
result.push(columnsIndex[key].last); |
|
|
|
const columnsFiltered = []; |
|
|
|
|
|
|
|
let count = 1; |
|
|
|
fs.createReadStream(this.filepath) |
|
|
|
.pipe(csvParser({ separator: ';' })) |
|
|
|
.on('headers', (headers) => { |
|
|
|
headers = headers.map(header => typeof header === 'string' ? header.trim() : header); |
|
|
|
|
|
|
|
const result = []; |
|
|
|
for (let key in columnsIndex) { |
|
|
|
columnsIndex[key].exist = headers.includes(columnsIndex[key].main); |
|
|
|
if ( columnsIndex[key].exist ) { |
|
|
|
columnsFiltered.push(columnsIndex[key].value); |
|
|
|
result.push(columnsIndex[key].last); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Emit a parse.start event with the url and filepath
|
|
|
|
emitter.emit('parse.start', { url: this.url, filepath: this.filepath, headers, result: result }); |
|
|
|
|
|
|
|
stream.write(result.join(';') + "\n"); |
|
|
|
fileStream.write(result.join(';') + "\n"); |
|
|
|
}) |
|
|
|
.on('data', (row) => { |
|
|
|
// Emit a parse.data event with the url, filepath and data
|
|
|
|
let result = this.processRow(row, columnsIndex, columnsFiltered); |
|
|
|
emitter.emit('parse.data', { url: this.url, filepath: this.filepath, data: row, result, index: count }); |
|
|
|
stream.write(result.join(';') + "\n"); |
|
|
|
fileStream.write(result.join(';') + "\n"); |
|
|
|
count++; |
|
|
|
}) |
|
|
|
.on('error', (err) => { |
|
|
|
// Emit a parse.error event with the error
|
|
|
|
emitter.emit('parse.error', { url: this.url, filepath: this.filepath, error: err.message }); |
|
|
|
|
|
|
|
fileStream.close(); |
|
|
|
fs.unlink(this.generatedpath, () => {}); |
|
|
|
}) |
|
|
|
.on('end', () => { |
|
|
|
// Emit a parse.end event with the url and filepath
|
|
|
|
stream.end(); |
|
|
|
fileStream.close(); |
|
|
|
emitter.emit('parse.end', { url: this.url, filepath: this.filepath, count: count - 1, generated: path.basename(this.generatedpath) }); |
|
|
|
}); |
|
|
|
|
|
|
|
// Emit a parse.start event with the url and filepath
|
|
|
|
emitter.emit('parse.start', { url: this.url, filepath: this.filepath, headers, result: result }); |
|
|
|
|
|
|
|
return stream; |
|
|
|
fileStream.write(result.join(';') + "\n"); |
|
|
|
}) |
|
|
|
.on('data', (row) => { |
|
|
|
// Emit a parse.data event with the url, filepath and data
|
|
|
|
let result = this.processRow(row, columnsIndex, columnsFiltered); |
|
|
|
emitter.emit('parse.data', { url: this.url, filepath: this.filepath, data: row, result, index: count }); |
|
|
|
fileStream.write(result.join(';') + "\n"); |
|
|
|
count++; |
|
|
|
}) |
|
|
|
.on('error', (err) => { |
|
|
|
// Emit a parse.error event with the error
|
|
|
|
emitter.emit('parse.error', { url: this.url, filepath: this.filepath, error: err.message }); |
|
|
|
|
|
|
|
fileStream.close(); |
|
|
|
reject(err); |
|
|
|
fs.unlink(this.generatedpath, () => {}); |
|
|
|
}) |
|
|
|
.on('end', () => { |
|
|
|
// Emit a parse.end event with the url and filepath
|
|
|
|
fileStream.close(); |
|
|
|
resolve(this.generatedpath); |
|
|
|
emitter.emit('parse.end', { url: this.url, filepath: this.filepath, count: count - 1, generated: path.basename(this.generatedpath) }); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|