Skip to content

Commit

Permalink
move setWithHistoricalData to its own file
Browse files Browse the repository at this point in the history
  • Loading branch information
selaux committed Apr 6, 2014
1 parent 0f1a98b commit d2004b2
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 115 deletions.
50 changes: 2 additions & 48 deletions lib/modules/miners/bfgminer.js
Expand Up @@ -6,6 +6,7 @@ var net = require('net'),
moment = require('moment'),

Module = require('../../Module'),
setWithHistoricalData = require('../../utils/setWithHistoricalData'),
defaults = {
connected: false
};
Expand Down Expand Up @@ -249,54 +250,7 @@ module.exports = Module.extend({
}).value();
},

set: function () {
var self = this,
attributes = arguments[0],
historicalData = this.get('historicalData') || [],
now = new Date().getTime(),
lastUnfilledDataElement = _.findLast(historicalData, function (el) { return el.source !== undefined; }),
sourceElement = { timestamp: now, currentHashrate: attributes.currentHashrate },
fullElement = { source: [ sourceElement ] };

historicalData = historicalData.filter(function (observation) {
return observation.timestamp > (now - self.config.chartTimespan);
});
if (lastUnfilledDataElement) {
lastUnfilledDataElement.source.push(sourceElement);
} else {
if (historicalData.length > 1) {
fullElement.source.push({ timestamp: now, currentHashrate: historicalData[historicalData.length-2].currentHashrate });
}
historicalData.push(fullElement);
}
historicalData = this.buildMeanValue(historicalData);

attributes.historicalData = historicalData;
arguments[0] = attributes;

Module.prototype.set.apply(this, arguments);
},

buildMeanValue: function (historicalData) {
var self = this,
sum = function (sum, value) { return sum + value; },
lastDataElement = _.last(historicalData);

lastDataElement.currentHashrate = _(lastDataElement.source).pluck('currentHashrate').reduce(sum) / lastDataElement.source.length;
lastDataElement.timestamp = _(lastDataElement.source).pluck('timestamp').reduce(sum) / lastDataElement.source.length;

if (historicalData.length === 1) {
if (_.last(lastDataElement.source).timestamp - lastDataElement.source[0].timestamp > self.config.chartPrecision) {
delete lastDataElement.source;
}
} else {
if (lastDataElement.timestamp - historicalData[historicalData.length-2].timestamp > self.config.chartPrecision) {
delete lastDataElement.source;
}
}

return historicalData;
},
set: setWithHistoricalData([ 'currentHashrate' ], Module.prototype.set),

getViewData: function () {
return this.data.connected ? this.data : {};
Expand Down
54 changes: 54 additions & 0 deletions lib/utils/setWithHistoricalData.js
@@ -0,0 +1,54 @@
'use strict';

var _ = require('lodash');

function buildMeanValue(historicalData, config) {
var sum = function (sum, value) { return sum + value; },
lastDataElement = _.last(historicalData);

_.keys(lastDataElement.source[0]).forEach(function (key) {
lastDataElement[key] = _(lastDataElement.source).pluck(key).reduce(sum) / lastDataElement.source.length;
});

if (historicalData.length === 1) {
if (_.last(lastDataElement.source).timestamp - lastDataElement.source[0].timestamp > config.chartPrecision) {
delete lastDataElement.source;
}
} else {
if (lastDataElement.timestamp - historicalData[historicalData.length-2].timestamp > config.chartPrecision) {
delete lastDataElement.source;
}
}

return historicalData;
}

module.exports = function (attributesToSave, originalSetMethod) {
return function () {
var self = this,
attributes = arguments[0],
historicalData = this.get('historicalData') || [],
now = new Date().getTime(),
lastUnfilledDataElement = _.findLast(historicalData, function (el) { return el.source !== undefined; }),
sourceElement = _.extend({ timestamp: now }, _.pick(attributes, attributesToSave)),
fullElement = { source: [ sourceElement ] };

historicalData = historicalData.filter(function (observation) {
return observation.timestamp > (now - self.config.chartTimespan);
});
if (lastUnfilledDataElement) {
lastUnfilledDataElement.source.push(sourceElement);
} else {
if (historicalData.length > 1) {
fullElement.source.push(_.extend({ timestamp: now }, _.pick(attributes, attributesToSave)));
}
historicalData.push(fullElement);
}
historicalData = buildMeanValue(historicalData, self.config);

attributes.historicalData = historicalData;
arguments[0] = attributes;

originalSetMethod.apply(this, arguments);
};
};
67 changes: 0 additions & 67 deletions test/specs/lib/modules/miners/bfgminerSpec.js
Expand Up @@ -576,73 +576,6 @@ describe('modules/miners/bfgminer', function () {
expect(bfgAdapter.get('historicalData')[0].currentHashrate).to.equal(123);
expect(bfgAdapter.get('historicalData')[0].timestamp).to.be.within(now-1, now+1);
});

it('should append the currentHashrate value to existing historicalData', function () {
var bfgAdapter = new BfgAdapter({}, config),
now = new Date().getTime();

bfgAdapter.attributes.historicalData = [ { timestamp: now-5000, currentHashrate: 456 } ];
bfgAdapter.set({ currentHashrate: 789 });
expect(bfgAdapter.get('historicalData')).to.have.length(2);
expect(bfgAdapter.get('historicalData')[0]).to.deep.equal({ timestamp: now-5000, currentHashrate: 456 });
expect(bfgAdapter.get('historicalData')[1].currentHashrate).to.equal(789);
expect(bfgAdapter.get('historicalData')[1].timestamp).to.be.within(now-1, now+1);
});

it('should remove historical data that has been logged longer than the expiration date', function () {
var bfgAdapter = new BfgAdapter({}, _.defaults({ chartTimespan: 5000 }, config)),
now = new Date().getTime();

bfgAdapter.attributes.historicalData = [
{ timestamp: now-5100, currentHashrate: 456 },
{ timestamp: now-2500, currentHashrate: 456 }
];
bfgAdapter.set({ currentHashrate: 789 });
expect(bfgAdapter.get('historicalData')).to.have.length(2);
expect(bfgAdapter.get('historicalData')[0]).to.deep.equal({ timestamp: now-2500, currentHashrate: 456 });
expect(bfgAdapter.get('historicalData')[1].currentHashrate).to.equal(789);
expect(bfgAdapter.get('historicalData')[1].timestamp).to.be.within(now-1, now+1);
});
});

describe('buildMeanValue', function () {
it('should calculate the mean', function () {
var bfgAdapter = new BfgAdapter({}, _.defaults({ chartPrecision: 4, chartTimespan: Infinity }, config)),
historicalData;

historicalData = bfgAdapter.buildMeanValue([
{ source: [ { currentHashrate: 2, timestamp: 1 }, { currentHashrate: 4, timestamp: 3 } ] }
]);
expect(historicalData).to.have.length(1);
expect(historicalData[0].source).to.have.length(2);
expect(historicalData[0].currentHashrate).to.equal(3);
expect(historicalData[0].timestamp).to.equal(2);
});

it('should remove the source property if historicalData has one element and the source spans chartPrecision', function () {
var bfgAdapter = new BfgAdapter({}, _.defaults({ chartPrecision: 4, chartTimespan: Infinity }, config)),
historicalData;

historicalData = bfgAdapter.buildMeanValue([
{ source: [ { currentHashrate: 2, timestamp: 1 }, { currentHashrate: 4, timestamp: 6 } ] }
]);
expect(historicalData).to.have.length(1);
expect(historicalData[0].source).to.be.undefined;
expect(historicalData[0].currentHashrate).to.equal(3);
expect(historicalData[0].timestamp).to.equal(3.5);
});

it('should remove the source property if the last value and the value before last are more than chartPrecision apart', function () {
var bfgAdapter = new BfgAdapter({}, _.defaults({ chartPrecision: 4, chartTimespan: Infinity }, config)),
historicalData;

historicalData = bfgAdapter.buildMeanValue([
{ currentHashrate: 2, timestamp: 1 },
{ source: [ { currentHashrate: 4, timestamp: 6 } ] }
]);
expect(historicalData).to.have.length(2);
expect(historicalData[1].source).to.be.undefined;
});
});

});
161 changes: 161 additions & 0 deletions test/specs/lib/utils/setWithHistoricalDataSpec.js
@@ -0,0 +1,161 @@
'use strict';

var chai = require('chai'),
sinon = require('sinon'),
sinonChai = require('sinon-chai'),
expect = chai.expect,

setWithHistoricalData;

chai.use(sinonChai);

describe('utils/setWithHistoricalData', function () {
var now = new Date().getTime(),
module,
originalSet,
mixedInSet;

before(function () {
this.clock = sinon.useFakeTimers(now);
setWithHistoricalData = require('../../../../lib/utils/setWithHistoricalData');
});

after(function () {
this.clock.restore();
});

beforeEach(function () {
module = {
config: {
chartTimespan: 24 * 60 * 60 * 1000,
chartPrecision: 5 * 60 * 1000
}
};
originalSet = sinon.spy();
mixedInSet = setWithHistoricalData([ 'someAttr' ], originalSet);
module.get = sinon.stub();
});

describe('set', function () {
it('should add the currentHashrate value to historicalData', function () {
mixedInSet.call(module, { someAttr: 123 });
expect(originalSet).to.have.been.calledWith({
someAttr: 123,
historicalData: [
{ someAttr: 123, timestamp: now, source: [ { someAttr: 123, timestamp: now } ] }
]
});
});

it('should work with multiple attributes', function () {
mixedInSet = setWithHistoricalData([ 'someAttr', 'someOtherAttr' ], originalSet);
mixedInSet.call(module, { someAttr: 123, someOtherAttr: 456 });
expect(originalSet).to.have.been.calledWith({
someAttr: 123,
someOtherAttr: 456,
historicalData: [
{ someAttr: 123, someOtherAttr: 456, timestamp: now, source: [ { someAttr: 123, someOtherAttr: 456, timestamp: now } ] }
]
});
});

it('should append the currentHashrate value to existing historicalData', function () {
module.get.withArgs('historicalData').returns([
{ timestamp: now - 5000, someAttr: 456 }
]);
mixedInSet.call(module, { someAttr: 789 });

expect(originalSet).to.have.been.calledWith({
someAttr: 789,
historicalData: [
{ someAttr: 456, timestamp: now - 5000 },
{ someAttr: 789, timestamp: now, source: [ { someAttr: 789, timestamp: now } ] }
]
});
});

it('should remove historical data that has been logged longer than the expiration date', function () {
module.config.chartTimespan = 5000;
module.get.withArgs('historicalData').returns([
{ timestamp: now - 5100, someAttr: 456 },
{ timestamp: now - 2500, someAttr: 456 }
]);
mixedInSet.call(module, { someAttr: 789 });

expect(originalSet).to.have.been.calledWith({
someAttr: 789,
historicalData: [
{ someAttr: 456, timestamp: now - 2500 },
{ someAttr: 789, timestamp: now, source: [ { someAttr: 789, timestamp: now } ] }
]
});
});
});

describe('buildMeanValue', function () {
it('should calculate the mean', function () {
module.get.withArgs('historicalData').returns([
{
someAttr: 2,
timestamp: now - 1000,
source: [
{ someAttr: 2, timestamp: now - 1000 }
]
}
]);
mixedInSet.call(module, { someAttr: 4 });
expect(originalSet).to.have.been.calledWith({
someAttr: 4,
historicalData: [
{
someAttr: 3,
timestamp: now - 500,
source: [
{ someAttr: 2, timestamp: now - 1000 },
{ someAttr: 4, timestamp: now }
]
}
]
});
});

it('should remove the source property if historicalData has one element and the source spans chartPrecision', function () {
module.config.chartPrecision = 400;
module.get.withArgs('historicalData').returns([
{ someAttr: 2, timestamp: now - 1000, source: [ { someAttr: 2, timestamp: now - 1000 } ] }
]);
mixedInSet.call(module, { someAttr: 4 });
expect(originalSet).to.have.been.calledWith({
someAttr: 4,
historicalData: [
{
someAttr: 3,
timestamp: now - 500
}
]
});
});

it('should remove the source property if the last value and the value before last are more than chartPrecision apart', function () {
module.config.chartPrecision = 400;
module.get.withArgs('historicalData').returns([
{ someAttr: 2, timestamp: now - 500 },
{
someAttr: 2,
timestamp: now - 100,
source: [ { someAttr: 2, timestamp: now - 100 } ]
}
]);

mixedInSet.call(module, { someAttr: 4 });

expect(originalSet).to.have.been.calledWith({
someAttr: 4,
historicalData: [
{ someAttr: 2, timestamp: now - 500 },
{ someAttr: 3, timestamp: now - 50 }
]
});
});
});
});

0 comments on commit d2004b2

Please sign in to comment.