added files
This commit is contained in:
parent
6797c3db19
commit
3fc401ea79
2 changed files with 261 additions and 0 deletions
95
js/util/csv.js
Normal file
95
js/util/csv.js
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**
|
||||||
|
* Small module for exporting data to CSV.
|
||||||
|
*/
|
||||||
|
var _ = require('lodash');
|
||||||
|
var preconditions = require('preconditions').singleton();
|
||||||
|
var moment = require('../../lib/moment/moment');
|
||||||
|
|
||||||
|
var logger = require('../util/log.js');
|
||||||
|
var config = require('../../config');
|
||||||
|
|
||||||
|
|
||||||
|
var COL_DELIMITER = ',';
|
||||||
|
var ROW_DELIMITER = '\r\n';
|
||||||
|
|
||||||
|
function getValue(obj, property) {
|
||||||
|
if (_.isFunction(property)) {
|
||||||
|
try {
|
||||||
|
return property(obj);
|
||||||
|
} catch (err) {
|
||||||
|
if (_.isString(err)) return err;
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_.isObject(obj)) return undefined;
|
||||||
|
return obj.hasOwnProperty(property) ? obj[property] : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatValue(value, type, format) {
|
||||||
|
if (_.isUndefined(value) || _.isNull(value)) return '';
|
||||||
|
|
||||||
|
var r;
|
||||||
|
switch (type) {
|
||||||
|
default:
|
||||||
|
case 'string':
|
||||||
|
r = value.toString();
|
||||||
|
r.replace('"', '\\"');
|
||||||
|
break;
|
||||||
|
case 'date':
|
||||||
|
r = moment(value).format(format);
|
||||||
|
break;
|
||||||
|
case 'number':
|
||||||
|
r = value.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// escape when commas in values
|
||||||
|
if (r.indexOf(',') !== -1) {
|
||||||
|
r = '"' + r + '"';
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getHeader(descriptor) {
|
||||||
|
return _.map(descriptor.columns, function (col) {
|
||||||
|
return col.label || (_.isString(col.property) ? col.property : '') || '';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function processDataRow(data, descriptor) {
|
||||||
|
return _.map(descriptor.columns, function (col) {
|
||||||
|
var value = getValue(data, col.property);
|
||||||
|
var formatted = formatValue(value, col.type, col.format);
|
||||||
|
return formatted;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Convert json object to csv based on a descriptor
|
||||||
|
*
|
||||||
|
* @param {array} data - the array of json objects to convert to csv
|
||||||
|
* @param {object} descriptor - an object that parameterizes the conversion
|
||||||
|
* @param {function} cb - called with the resulting csv
|
||||||
|
*/
|
||||||
|
module.exports.toCsv = function(data, descriptor, cb) {
|
||||||
|
preconditions.shouldBeArray(data);
|
||||||
|
preconditions.shouldBeObject(descriptor);
|
||||||
|
preconditions.shouldBeArray(descriptor.columns);
|
||||||
|
preconditions.shouldBeFunction(cb);
|
||||||
|
|
||||||
|
var colDelimiter = descriptor.colDelimiter || COL_DELIMITER;
|
||||||
|
var rowDelimiter = descriptor.rowDelimiter || ROW_DELIMITER;
|
||||||
|
|
||||||
|
var rows = _.map(data, function (dataRow) {
|
||||||
|
return processDataRow(dataRow, descriptor);
|
||||||
|
});
|
||||||
|
|
||||||
|
var header = getHeader(descriptor);
|
||||||
|
rows.unshift(header);
|
||||||
|
|
||||||
|
var csv = _.reduce(rows, function (memo, row) {
|
||||||
|
return memo + row.join(colDelimiter) + rowDelimiter;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
return cb(null, csv);
|
||||||
|
};
|
||||||
166
test/util.csv.js
Normal file
166
test/util.csv.js
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
|
var chai = chai || require('chai');
|
||||||
|
var sinon = sinon || require('sinon');
|
||||||
|
var should = chai.should();
|
||||||
|
|
||||||
|
var csv = require('../js/util/csv')
|
||||||
|
var moment = require('../lib/moment/moment');
|
||||||
|
|
||||||
|
describe('csv utils', function() {
|
||||||
|
it('should convert simple json', function(done) {
|
||||||
|
var data = [
|
||||||
|
{ name: 'Lennon John', age: 40, lastLogin: moment(1417608870000), },
|
||||||
|
{ name: 'Cobain, Kurt', age: 27, lastLogin: moment('2014-11-01'), },
|
||||||
|
];
|
||||||
|
|
||||||
|
var descriptor = {
|
||||||
|
columns: [
|
||||||
|
{ label: 'Name', property: 'name', type: 'string' },
|
||||||
|
{ label: 'Age', property: 'age', type: 'number' },
|
||||||
|
{ property: 'lastLogin', type: 'date' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
csv.toCsv(data, descriptor, function (err, res) {
|
||||||
|
res.should.equal('Name,Age,lastLogin\r\nLennon John,40,2014-12-03T09:14:30-03:00\r\n"Cobain, Kurt",27,2014-11-01T00:00:00-03:00\r\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should handle empty data', function(done) {
|
||||||
|
var data = [];
|
||||||
|
|
||||||
|
var descriptor = {
|
||||||
|
columns: [
|
||||||
|
{ label: 'Name', property: 'name', type: 'string' },
|
||||||
|
{ label: 'Age', property: 'age', type: 'number' },
|
||||||
|
{ property: 'lastLogin', type: 'date' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
csv.toCsv(data, descriptor, function (err, res) {
|
||||||
|
res.should.equal('Name,Age,lastLogin\r\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should handle null row in data', function(done) {
|
||||||
|
var data = [
|
||||||
|
{ name: 'John', age: 40 },
|
||||||
|
null,
|
||||||
|
{ name: 'Kurt', age: 27 },
|
||||||
|
];
|
||||||
|
|
||||||
|
var descriptor = {
|
||||||
|
columns: [
|
||||||
|
{ label: 'Name', property: 'name', type: 'string' },
|
||||||
|
{ label: 'Age', property: 'age', type: 'number' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
csv.toCsv(data, descriptor, function (err, res) {
|
||||||
|
res.should.equal('Name,Age\r\nJohn,40\r\n,\r\nKurt,27\r\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should format dates', function(done) {
|
||||||
|
var data = [
|
||||||
|
{ name: 'John', age: 40, lastLogin: moment(1417608870000), },
|
||||||
|
{ name: 'Kurt', age: 27, lastLogin: moment('2014-11-01'), },
|
||||||
|
];
|
||||||
|
|
||||||
|
var descriptor = {
|
||||||
|
columns: [
|
||||||
|
{ property: 'name', type: 'string' },
|
||||||
|
{ property: 'age', type: 'number' },
|
||||||
|
{ property: 'lastLogin', type: 'date', format: 'YYYY MM DD' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
csv.toCsv(data, descriptor, function (err, res) {
|
||||||
|
res.should.equal('name,age,lastLogin\r\nJohn,40,2014 12 03\r\nKurt,27,2014 11 01\r\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should compute values from function properties', function(done) {
|
||||||
|
var data = [
|
||||||
|
{ name: 'John', payments: 400, withdrawals: 300, },
|
||||||
|
{ name: 'Kurt', payments: 270.5, withdrawals: 200, },
|
||||||
|
];
|
||||||
|
|
||||||
|
var descriptor = {
|
||||||
|
columns: [
|
||||||
|
{ property: 'name', type: 'string' },
|
||||||
|
{ label: 'Balance', property: function (obj) { return obj.payments - obj.withdrawals; }, type: 'number' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
csv.toCsv(data, descriptor, function (err, res) {
|
||||||
|
res.should.equal('name,Balance\r\nJohn,100\r\nKurt,70.5\r\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should not fail on error from calculated values', function(done) {
|
||||||
|
var data = [
|
||||||
|
{ name: 'John', error: 0 },
|
||||||
|
{ name: 'Kurt', error: 1 },
|
||||||
|
];
|
||||||
|
|
||||||
|
var descriptor = {
|
||||||
|
columns: [{
|
||||||
|
property: 'name',
|
||||||
|
type: 'string'
|
||||||
|
}, {
|
||||||
|
label: 'Error',
|
||||||
|
property: function(obj) {
|
||||||
|
if (obj.error) {
|
||||||
|
throw 'dummy error';
|
||||||
|
} else {
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, ],
|
||||||
|
};
|
||||||
|
csv.toCsv(data, descriptor, function (err, res) {
|
||||||
|
res.should.equal('name,Error\r\nJohn,ok\r\nKurt,dummy error\r\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should use blank label if label not specified for computed property', function(done) {
|
||||||
|
var data = [
|
||||||
|
{ name: 'John', payments: 400, withdrawals: 300, },
|
||||||
|
{ name: 'Kurt', payments: 270.5, withdrawals: 200, },
|
||||||
|
];
|
||||||
|
|
||||||
|
var descriptor = {
|
||||||
|
columns: [
|
||||||
|
{ property: 'name', type: 'string' },
|
||||||
|
{ property: function (obj) { return obj.payments - obj.withdrawals; }, type: 'number' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
csv.toCsv(data, descriptor, function (err, res) {
|
||||||
|
res.should.equal('name,\r\nJohn,100\r\nKurt,70.5\r\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should handle non existent properties', function(done) {
|
||||||
|
var data = [
|
||||||
|
{ name: 'John', age: 40 },
|
||||||
|
{ name: 'Kurt', age: 27 },
|
||||||
|
];
|
||||||
|
|
||||||
|
var descriptor = {
|
||||||
|
columns: [
|
||||||
|
{ property: 'name', type: 'string' },
|
||||||
|
{ property: 'age', type: 'number' },
|
||||||
|
{ property: 'lastLogin', type: 'date', format: 'YYYY MM DD' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
csv.toCsv(data, descriptor, function (err, res) {
|
||||||
|
res.should.equal('name,age,lastLogin\r\nJohn,40,\r\nKurt,27,\r\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue