require('../../support/test_helper'); var assert = require('../../support/assert'); var TestClient = require('../../support/test-client'); var moment = require('moment'); function createMapConfig(layers, dataviews, analysis) { return { version: '1.5.0', layers: layers, dataviews: dataviews || {}, analyses: analysis || [] }; } describe('histogram-dataview', function() { afterEach(function(done) { if (this.testClient) { this.testClient.drain(done); } else { done(); } }); var mapConfig = createMapConfig( [ { "type": "cartodb", "options": { "source": { "id": "2570e105-7b37-40d2-bdf4-1af889598745" }, "cartocss": "#points { marker-width: 10; marker-fill: red; }", "cartocss_version": "2.3.0" } } ], { pop_max_histogram: { source: { id: '2570e105-7b37-40d2-bdf4-1af889598745' }, type: 'histogram', options: { column: 'x' } } }, [ { "id": "2570e105-7b37-40d2-bdf4-1af889598745", "type": "source", "params": { "query": "select null::geometry the_geom_webmercator, x from generate_series(0,1000) x" } } ] ); it('should get bin_width right when max > min in filter', function(done) { var params = { bins: 10, start: 1e3, end: 0 }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview('pop_max_histogram', params, function(err, dataview) { assert.ok(!err, err); assert.equal(dataview.type, 'histogram'); assert.ok(dataview.bin_width > 0, 'Unexpected bin width: ' + dataview.bin_width); dataview.bins.forEach(function(bin) { assert.ok(bin.min <= bin.max, 'bin min < bin max: ' + JSON.stringify(bin)); }); done(); }); }); it('should cast all overridable params to numbers', function(done) { var params = { bins: '256 AS other, (select 256 * 2) AS bins_number--', start: 1e3, end: 0, response: TestClient.RESPONSE.ERROR }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview('pop_max_histogram', params, function(err, res) { assert.ok(!err, err); assert.ok(res.errors); assert.equal(res.errors.length, 1); assert.ok(res.errors[0].match(/Invalid number format for parameter 'bins'/)); done(); }); }); }); describe('histogram-dataview for date column type', function() { afterEach(function(done) { if (this.testClient) { this.testClient.drain(done); } else { done(); } }); var mapConfig = createMapConfig( [ { "type": "cartodb", "options": { "source": { "id": "date-histogram-source" }, "cartocss": "#points { marker-width: 10; marker-fill: red; }", "cartocss_version": "2.3.0" } } ], { date_histogram: { source: { id: 'date-histogram-source' }, type: 'histogram', options: { column: 'd', aggregation: 'month', timezone: -14400 // EDT Eastern Daylight Time (GMT-4) in seconds } }, date_histogram_tz: { source: { id: 'date-histogram-source-tz' }, type: 'histogram', options: { column: 'd', aggregation: 'month', timezone: -14400 // EDT Eastern Daylight Time (GMT-4) in seconds } } }, [ { "id": "date-histogram-source", "type": "source", "params": { "query": [ "select null::geometry the_geom_webmercator, date AS d", "from generate_series(", "'2007-02-15 01:00:00'::timestamp, '2008-04-09 01:00:00'::timestamp, '1 day'::interval", ") date" ].join(' ') } }, { "id": "date-histogram-source-tz", "type": "source", "params": { "query": [ "select null::geometry the_geom_webmercator, date AS d", "from generate_series(", "'2007-02-15 01:00:00'::timestamptz, '2008-04-09 01:00:00'::timestamptz, '1 day'::interval", ") date" ].join(' ') } } ] ); var dateHistogramsUseCases = [{ desc: 'supporting timestamp with timezone', dataviewId: 'date_histogram_tz' }, { desc: 'supporting timestamp without timezone', dataviewId: 'date_histogram' }]; dateHistogramsUseCases.forEach(function (test) { it('should create a date histogram aggregated in months (EDT) ' + test.desc, function (done) { var TIMEZONE_EDT_IN_MINUTES = -4 * 60; // EDT Eastern Daylight Time (GMT-4) in minutes this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview(test.dataviewId, {}, function(err, dataview) { assert.ok(!err, err); assert.equal(dataview.type, 'histogram'); assert.ok(dataview.bin_width > 0, 'Unexpected bin width: ' + dataview.bin_width); assert.equal(dataview.bins.length, 15); var initialTimestamp = '2007-02-01T00:00:00-04:00'; // EDT midnight var binsStartInMilliseconds = dataview.bins_start * 1000; var binsStartFormatted = moment.utc(binsStartInMilliseconds) .utcOffset(TIMEZONE_EDT_IN_MINUTES) .format(); assert.equal(binsStartFormatted, initialTimestamp); dataview.bins.forEach(function(bin, index) { var binTimestampExpected = moment.utc(initialTimestamp) .utcOffset(TIMEZONE_EDT_IN_MINUTES) .add(index, 'month') .format(); var binsTimestampInMilliseconds = bin.timestamp * 1000; var binTimestampFormatted = moment.utc(binsTimestampInMilliseconds) .utcOffset(TIMEZONE_EDT_IN_MINUTES) .format(); assert.equal(binTimestampFormatted, binTimestampExpected); assert.ok(bin.timestamp <= bin.min, 'bin timestamp < bin min: ' + JSON.stringify(bin)); assert.ok(bin.min <= bin.max, 'bin min < bin max: ' + JSON.stringify(bin)); }); done(); }); }); it('should override aggregation in weeks ' + test.desc, function (done) { var params = { aggregation: 'week' }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview(test.dataviewId, params, function (err, dataview) { assert.ok(!err, err); assert.equal(dataview.type, 'histogram'); assert.ok(dataview.bin_width > 0, 'Unexpected bin width: ' + dataview.bin_width); assert.equal(dataview.bins.length, 61); dataview.bins.forEach(function (bin) { assert.ok(bin.min <= bin.max, 'bin min < bin max: ' + JSON.stringify(bin)); }); done(); }); }); it('should override start and end ' + test.desc, function (done) { var params = { start: 1180659600, // 2007-06-01 01:00:00 end: 1193792400 // 2007-10-31 01:00:00 }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview(test.dataviewId, params, function (err, dataview) { assert.ok(!err, err); assert.equal(dataview.type, 'histogram'); assert.ok(dataview.bin_width > 0, 'Unexpected bin width: ' + dataview.bin_width); assert.equal(dataview.bins.length, 6); dataview.bins.forEach(function (bin) { assert.ok(bin.min <= bin.max, 'bin min < bin max: ' + JSON.stringify(bin)); }); done(); }); }); it('should aggregate histogram overriding default timezone to CEST ' + test.desc, function (done) { var TIMEZONE_CEST_IN_SECONDS = 2 * 3600; // Central European Summer Time (Daylight Saving Time) var TIMEZONE_CEST_IN_MINUTES = 2 * 60; // Central European Summer Time (Daylight Saving Time) var params = { timezone: TIMEZONE_CEST_IN_SECONDS }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview(test.dataviewId, params, function (err, dataview) { assert.ok(!err, err); assert.equal(dataview.type, 'histogram'); assert.ok(dataview.bin_width > 0, 'Unexpected bin width: ' + dataview.bin_width); assert.equal(dataview.bins.length, 15); var initialTimestamp = '2007-02-01T00:00:00+02:00'; // CEST midnight var binsStartInMilliseconds = dataview.bins_start * 1000; var binsStartFormatted = moment.utc(binsStartInMilliseconds) .utcOffset(TIMEZONE_CEST_IN_MINUTES) .format(); assert.equal(binsStartFormatted, initialTimestamp); dataview.bins.forEach(function (bin, index) { var binTimestampExpected = moment.utc(initialTimestamp) .utcOffset(TIMEZONE_CEST_IN_MINUTES) .add(index, 'month') .format(); var binsTimestampInMilliseconds = bin.timestamp * 1000; var binTimestampFormatted = moment.utc(binsTimestampInMilliseconds) .utcOffset(TIMEZONE_CEST_IN_MINUTES) .format(); assert.equal(binTimestampFormatted, binTimestampExpected); assert.ok(bin.timestamp <= bin.min, 'bin timestamp < bin min: ' + JSON.stringify(bin)); assert.ok(bin.min <= bin.max, 'bin min < bin max: ' + JSON.stringify(bin)); }); done(); }); }); it('should aggregate histogram overriding default timezone to UTC/GMT ' + test.desc, function (done) { var TIMEZONE_UTC_IN_SECONDS = 0 * 3600; // UTC var TIMEZONE_UTC_IN_MINUTES = 0 * 60; // UTC var params = { timezone: TIMEZONE_UTC_IN_SECONDS }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview(test.dataviewId, params, function (err, dataview) { assert.ok(!err, err); assert.equal(dataview.type, 'histogram'); assert.ok(dataview.bin_width > 0, 'Unexpected bin width: ' + dataview.bin_width); assert.equal(dataview.bins.length, 15); var initialTimestamp = '2007-02-01T00:00:00Z'; // UTC midnight var binsStartInMilliseconds = dataview.bins_start * 1000; var binsStartFormatted = moment.utc(binsStartInMilliseconds) .utcOffset(TIMEZONE_UTC_IN_MINUTES) .format(); assert.equal(binsStartFormatted, initialTimestamp); dataview.bins.forEach(function (bin, index) { var binTimestampExpected = moment.utc(initialTimestamp) .utcOffset(TIMEZONE_UTC_IN_MINUTES) .add(index, 'month') .format(); var binsTimestampInMilliseconds = bin.timestamp * 1000; var binTimestampFormatted = moment.utc(binsTimestampInMilliseconds) .utcOffset(TIMEZONE_UTC_IN_MINUTES) .format(); assert.equal(binTimestampFormatted, binTimestampExpected); assert.ok(bin.timestamp <= bin.min, 'bin timestamp < bin min: ' + JSON.stringify(bin)); assert.ok(bin.min <= bin.max, 'bin min < bin max: ' + JSON.stringify(bin)); }); done(); }); }); it('should aggregate histogram using "quarter" aggregation ' + test.desc, function (done) { var TIMEZONE_UTC_IN_SECONDS = 0 * 3600; // UTC var TIMEZONE_UTC_IN_MINUTES = 0 * 60; // UTC var params = { timezone: TIMEZONE_UTC_IN_SECONDS, aggregation: 'quarter' }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview(test.dataviewId, params, function (err, dataview) { assert.ok(!err, err); assert.equal(dataview.type, 'histogram'); assert.ok(dataview.bin_width > 0, 'Unexpected bin width: ' + dataview.bin_width); assert.equal(dataview.bins.length, 6); var initialTimestamp = '2007-01-01T00:00:00Z'; // UTC midnight var binsStartInMilliseconds = dataview.bins_start * 1000; var binsStartFormatted = moment.utc(binsStartInMilliseconds) .utcOffset(TIMEZONE_UTC_IN_MINUTES) .format(); assert.equal(binsStartFormatted, initialTimestamp); dataview.bins.forEach(function (bin, index) { var binTimestampExpected = moment.utc(initialTimestamp) .utcOffset(TIMEZONE_UTC_IN_MINUTES) .add(index * 3, 'month') .format(); var binsTimestampInMilliseconds = bin.timestamp * 1000; var binTimestampFormatted = moment.utc(binsTimestampInMilliseconds) .utcOffset(TIMEZONE_UTC_IN_MINUTES) .format(); assert.equal(binTimestampFormatted, binTimestampExpected); assert.ok(bin.timestamp <= bin.min, 'bin timestamp < bin min: ' + JSON.stringify(bin)); assert.ok(bin.min <= bin.max, 'bin min < bin max: ' + JSON.stringify(bin)); }); done(); }); }); it('bins_count should be equal to bins length filtered by start and end ' + test.desc, function (done) { var TIMEZONE_UTC_IN_SECONDS = 0 * 3600; // UTC var params = { timezone: TIMEZONE_UTC_IN_SECONDS, aggregation: 'quarter', start: 1167609600, // 2007-01-01T00:00:00Z, first bin start end: 1214870399 // 2008-06-30T23:59:59Z, last bin end }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview(test.dataviewId, params, function (err, dataview) { assert.ifError(err); assert.equal(dataview.type, 'histogram'); assert.equal(dataview.bins.length, 6); assert.equal(dataview.bins_count, 6); assert.equal(dataview.bins_count, dataview.bins.length); done(); }); }); it('bins_count should be greater than bins length filtered by start and end ' + test.desc, function (done) { var TIMEZONE_UTC_IN_SECONDS = 0 * 3600; // UTC var params = { timezone: TIMEZONE_UTC_IN_SECONDS, aggregation: 'quarter', start: 1167609600, // 2007-01-01T00:00:00Z, first bin start end: 1214870400 // 2008-07-01T00:00:00Z, start the next bin to the last }; this.testClient = new TestClient(mapConfig, 1234); this.testClient.getDataview(test.dataviewId, params, function (err, dataview) { assert.ifError(err); assert.equal(dataview.type, 'histogram'); assert.equal(dataview.bins.length, 6); assert.equal(dataview.bins_count, 7); assert.ok(dataview.bins_count > dataview.bins.length); done(); }); }); }); });