diff --git a/logic/tracks.js b/logic/tracks.js index 3211758..18e20f9 100644 --- a/logic/tracks.js +++ b/logic/tracks.js @@ -1,4 +1,5 @@ const csvParse = require('csv-parse/lib/sync'); +const csvStringify = require('csv-stringify/lib/sync'); function _parseFloat(token) { if (typeof token !== 'string') { @@ -29,7 +30,8 @@ function _parseFloat(token) { } function _parseInt(token) { - const asFloat = parseFloat(token); + const asFloat = _parseFloat(token); + if (asFloat !== null) { return Math.floor(asFloat); } else { @@ -274,6 +276,28 @@ function normalizeUserAgent(userAgent) { return null; } +function buildObsver1(points) { + return csvStringify(points, { + columns: [ + { key: 'date', header: 'Date' }, + { key: 'time', header: 'Time' }, + { key: 'latitude', header: 'Latitude' }, + { key: 'longitude', header: 'Longitude' }, + { key: 'course', header: 'Course' }, + { key: 'speed', header: 'Speed' }, + { key: 'd1', header: 'Right' }, + { key: 'd2', header: 'Left' }, + { key: 'flag', header: 'Confirmed' }, + { key: 'private', header: 'insidePrivacyArea' }, + ], + cast: { + boolean: (v) => (v ? '1' : '0'), + }, + delimiter: ';', + header: true, + }); +} + module.exports = { detectFormat, normalizeUserAgent, @@ -281,4 +305,5 @@ module.exports = { parseObsver2, parseTrackPoints, replaceDollarNewlinesHack, + buildObsver1, }; diff --git a/logic/tracks.test.js b/logic/tracks.test.js index b84a50b..d2d933c 100644 --- a/logic/tracks.test.js +++ b/logic/tracks.test.js @@ -1,4 +1,5 @@ const { + buildObsver1, detectFormat, normalizeUserAgent, parseObsver1, @@ -140,3 +141,31 @@ describe('normalizeUserAgent', () => { } }); }); + +describe('buildObsver1', () => { + it('is a function', () => { + expect(typeof normalizeUserAgent).toBe('function'); + }); + + it('transforms properly back and forth', () => { + const inputString = replaceDollarNewlinesHack(test1); + + const points1 = Array.from(parseObsver1(inputString)); + const builtString = buildObsver1(points1); + const points2 = Array.from(parseObsver1(builtString)); + + expect(points2).toEqual(points1); + }); + + it('produces a header', () => { + const builtString = buildObsver1([]); + expect(builtString).toBe('Date;Time;Latitude;Longitude;Course;Speed;Right;Left;Confirmed;insidePrivacyArea\n'); + }); + + it('produces empty rows', () => { + const builtString = buildObsver1([{}]); + expect(builtString).toBe( + 'Date;Time;Latitude;Longitude;Course;Speed;Right;Left;Confirmed;insidePrivacyArea\n;;;;;;;;;\n', + ); + }); +}); diff --git a/package-lock.json b/package-lock.json index 677cdf9..52eac42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2163,6 +2163,11 @@ "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.14.1.tgz", "integrity": "sha512-4wmcO7QbWtDAncGFaBwlWFPhEN4Akr64IbM4zvDwEOFekI8blLc04Nw7XjQjtSNy+3AUAgBgtUa9nWo5Cq89Xg==" }, + "csv-stringify": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.5.3.tgz", + "integrity": "sha512-JKG8vIHpWPzdilp2SAmvjmAiIhD+XGKGdhZBGi8QIECgJAsFr7k5CmJIW2QkSxBBsctvmojM25s+UINzQ5NLTg==" + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", diff --git a/package.json b/package.json index b1fe17d..751718f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "connect-busboy": "0.0.2", "cors": "2.8.5", "csv-parse": "^4.14.1", + "csv-stringify": "^5.5.3", "ejs": "^3.1.5", "errorhandler": "1.5.1", "express": "4.17.1",