mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 08:17:09 +00:00
147 lines
4.1 KiB
JavaScript
147 lines
4.1 KiB
JavaScript
const Busboy = require('busboy');
|
|
const { parse } = require('csv-parse/sync');
|
|
|
|
async function middleware(req, res, next) {
|
|
const contentType = req.headers['content-type'] || '';
|
|
let csvText = null;
|
|
let filename = null;
|
|
|
|
if (req.params.filename && contentType.startsWith('text/csv')) {
|
|
csvText = typeof req.body === 'string' ? req.body : req.body.toString('utf8');
|
|
filename = req.params.filename;
|
|
processCsv();
|
|
} else if (contentType.startsWith('multipart/form-data')) {
|
|
const busboy = Busboy({ headers: req.headers });
|
|
let found = false;
|
|
busboy.on('file', (name, file, info) => {
|
|
if (found) {
|
|
file.resume();
|
|
return;
|
|
}
|
|
if (info.mimeType === 'text/csv') {
|
|
found = true;
|
|
filename = info.filename || 'unnamed.csv';
|
|
csvText = '';
|
|
file.setEncoding('utf8');
|
|
file.on('data', (data) => { csvText += data; });
|
|
file.on('end', () => {});
|
|
} else {
|
|
file.resume();
|
|
}
|
|
});
|
|
busboy.on('field', () => {}); // Ignore fields
|
|
busboy.on('finish', () => {
|
|
if (!found) {
|
|
return next();
|
|
}
|
|
processCsv();
|
|
});
|
|
req.pipe(busboy);
|
|
return;
|
|
} else {
|
|
return next();
|
|
}
|
|
|
|
function processCsv() {
|
|
let records;
|
|
try {
|
|
records = parse(csvText, {
|
|
relax_quotes: true,
|
|
quote: '"',
|
|
escape: '"',
|
|
skip_empty_lines: true,
|
|
trim: true
|
|
});
|
|
} catch (e) {
|
|
return res.status(400).json({ error: 'Invalid CSV' });
|
|
}
|
|
if (!records.length) {
|
|
return res.status(400).json({ error: 'Empty CSV' });
|
|
}
|
|
const headers = records[0].map(h => h.toLowerCase().trim());
|
|
const rows = records.slice(1);
|
|
let lastDate = null;
|
|
let lastTime = null;
|
|
const currentDate = new Date().toISOString().slice(0, 10);
|
|
const currentTime = new Date().toISOString().slice(11, 19);
|
|
const events = [];
|
|
for (let row of rows) {
|
|
let object = { labels: [] };
|
|
for (let k = 0; k < headers.length; k++) {
|
|
let key = headers[k];
|
|
let val = row[k] ? row[k].trim() : '';
|
|
if (!key) continue;
|
|
if (['remarks', 'event', 'comment', 'comments', 'text'].includes(key)) {
|
|
object.remarks = val;
|
|
} else if (key === 'label') {
|
|
if (val) object.labels.push(val);
|
|
} else if (key === 'labels') {
|
|
if (val) object.labels.push(...val.split(';').map(l => l.trim()).filter(l => l));
|
|
} else if (key === 'sequence' || key === 'seq') {
|
|
if (val) object.sequence = Number(val);
|
|
} else if (['point', 'shot', 'shotpoint'].includes(key)) {
|
|
if (val) object.point = Number(val);
|
|
} else if (key === 'date') {
|
|
object.date = val;
|
|
} else if (key === 'time') {
|
|
object.time = val;
|
|
} else if (key === 'timestamp') {
|
|
object.timestamp = val;
|
|
} else if (key === 'latitude') {
|
|
object.latitude = parseFloat(val);
|
|
} else if (key === 'longitude') {
|
|
object.longitude = parseFloat(val);
|
|
}
|
|
}
|
|
if (!object.remarks) continue;
|
|
let useSeqPoint = Number.isFinite(object.sequence) && Number.isFinite(object.point);
|
|
let tstamp = null;
|
|
if (!useSeqPoint) {
|
|
if (object.timestamp) {
|
|
tstamp = new Date(object.timestamp);
|
|
}
|
|
if (!tstamp || isNaN(tstamp.getTime())) {
|
|
let dateStr = object.date || lastDate || currentDate;
|
|
let timeStr = object.time || lastTime || currentTime;
|
|
if (timeStr.length === 5) timeStr += ':00';
|
|
let full = `${dateStr}T${timeStr}.000Z`;
|
|
tstamp = new Date(full);
|
|
if (isNaN(tstamp.getTime())) continue;
|
|
}
|
|
if (object.date) lastDate = object.date;
|
|
if (object.time) lastTime = object.time;
|
|
}
|
|
let event = {
|
|
remarks: object.remarks,
|
|
labels: object.labels,
|
|
meta: {
|
|
author: "*CSVImport*",
|
|
"*CSVImport*": {
|
|
filename,
|
|
tstamp: new Date().toISOString()
|
|
}
|
|
}
|
|
};
|
|
if (!isNaN(object.latitude) && !isNaN(object.longitude)) {
|
|
event.meta.geometry = {
|
|
type: "Point",
|
|
coordinates: [object.longitude, object.latitude]
|
|
};
|
|
}
|
|
if (useSeqPoint) {
|
|
event.sequence = object.sequence;
|
|
event.point = object.point;
|
|
} else if (tstamp) {
|
|
event.tstamp = tstamp.toISOString();
|
|
} else {
|
|
continue;
|
|
}
|
|
events.push(event);
|
|
}
|
|
req.body = events;
|
|
next();
|
|
}
|
|
}
|
|
|
|
module.exports = middleware;
|