diff --git a/client/lib/databasable.js b/client/lib/databasable.js new file mode 100644 index 0000000..673d0cb --- /dev/null +++ b/client/lib/databasable.js @@ -0,0 +1,43 @@ +const DEFAULTS = { + adapter: '' +}; + +var databasable = { + + accessDb: function(db_name, opts){ + var databasable = this; + opts = Object.assign(Object.assign({}, DEFAULTS), opts || {}); + return new Promise((fnResolve, fnReject){ + if (!databasable.db) { + databasable.db = new Loki(db_name, opts); + databasable.db.loadDatabase({}, ()=>{ + fnResolve(databasable.db); + }); + } else { fnResolve(databasable.db); } + }); + }, + + closeDb: function(){ + var databasable = this; + if (databasable.db){ + databasable.db.save(); + databasable.db.close(); + databasable.db = undefined; + } + }, + + collection(collection_name, options){ + var databasable = this; + databasable.accessDb() + .then((db)=>{ + var collection = db.getCollection(collection_name) + if (!collection){ + collection = db.addCollection(collection_name, options); + } + return collection; + }); + } + +}; + +export default databaseable; diff --git a/client/models/house.js b/client/models/house.js index ef105a7..d4bb143 100644 --- a/client/models/house.js +++ b/client/models/house.js @@ -11,35 +11,39 @@ import MathUtil from './../../shared/utils/math' class House { + // must be initiated with a dataset already in Loki database (not directly JSON). constructor(data){ var house = this; - House.store.set(data.id, house); house.data = data; - house.energy_data = []; - house.power_data = []; - house.energy_data_store = new Map(); - house.power_data_store = new Map(); - - house.initDatabase(); + Object.assign(house, Databasable); } - get react_key(){ + get scoped_id(){ return `house-${this.data.id}`; } + save(){ + var house = this; + return House.collection() + .then((house_collection)=>{ + return house_collection.update(house.data); + }) + } + ensurePowerData(opts){ opts = extend({ start_date: undefined, end_date: undefined }, opts || {}); - var house = this; + var house = this, + query_ranges = DateUtil.dateOverlaps([opts.start_date, opts.end_date], house.data.query_dates]); - // check mins and maxes. - house.initCollection(PowerDatum.NAME).then(()=>{ - house.power_datum_collection. - }); - return Promise.resolve(cache); + + house.collection(PowerDatum.name, {}) + .then((power_collection)=>{ + + }) } getPowerData(params){ @@ -115,20 +119,20 @@ class House { static accessDb(){ return new Promise((fnResolve, fnReject){ - if (!this.db) { - this.db = new Loki('houses', adapter: ''); - this.db.loadDatabase({}, ()=>{ - fnResolve(this.db); + if (!House.db) { + House.db = new Loki('houses', adapter: ''); + House.db.loadDatabase({}, ()=>{ + fnResolve(House.db); }); - } else { fnResolve(this.db); } + } else { fnResolve(House.db); } }); } static closeDb(){ - if (this.db){ - this.db.save(); - this.db.close(); - this.db = undefined; + if (House.db){ + House.db.save(); + House.db.close(); + House.db = undefined; } } @@ -174,6 +178,4 @@ class House { } -House.store = new Map(); - export default House; diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 46415ed..857be1c 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -40,6 +40,8 @@ gulp.task('build', function(done) { env = 'production'; } else if (yargs.argv.design){ env = 'design'; + } else if (yargs.argv.test){ + env = 'test'; } else { throw new gutil.PluginError("webpack", "Must include '--production' or '--design' option."); } @@ -52,5 +54,15 @@ gulp.task('build', function(done) { })); done(); }); - +}); + +gulp.task('test', function(done) { + var Jasmine = require('jasmine'); + var jasmine = new Jasmine(); + + jasmine.loadConfigFile('test/jasmine.json'); + jasmine.configureDefaultReporter({ + showColors: true + }); + jasmine.execute(); }); diff --git a/package.json b/package.json index 1922ea9..f1996bf 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,6 @@ "babel-preset-stage-0": "6.3.13", "babel-core": "6.3.21", "babel-loader": "6.2.0", - "babel-preset-es2015": "6.3.13", - "babel-preset-react": "6.3.13", - "babel-preset-stage-0": "6.3.13", "express": "4.13.3", "react": "0.14.3", "react-dom": "0.14.3", @@ -40,17 +37,22 @@ "style-loader": "^0.12.3", "json-loader": "0.5.4", "node-sass": "3.4.2", - "moment-timezone":"0.5.0", + "moment-timezone": "0.5.0", "yargs": "3.32.0", "extend": "3.0.0", "through2": "2.0.1", - "lokijs": "1.3.11" - }, - "devDependencies": { - "gulp": "^3.9.0", - "gulp-util": "3.0.7", + "lokijs": "1.3.11", "babel-cli": "^6.3.17", "babel-standalone": "6.4.4", - "react-templates": "0.4.1" + "gulp": "^3.9.0", + "gulp-util": "3.0.7", + "jasmine-core": "2.4.1", + "karma": "^0.13.19", + "karma-babel-preprocessor": "^6.0.1", + "karma-chrome-launcher": "^0.2.2", + "karma-jasmine": "^0.3.6", + "react-templates": "0.4.1", + "requirejs": "~2.1", + "jasmine-es6": "0.1.4" } } diff --git a/shared/utils/date.js b/shared/utils/date.js new file mode 100644 index 0000000..93b6815 --- /dev/null +++ b/shared/utils/date.js @@ -0,0 +1,77 @@ +class DateUtil { + + static addRange(new_range, ranges){ + var gaps_filled = [], new_ranges = [], + start = new_range[0], end = new_range[1]; + if (start === undefined && end === undefined && ranges.length === 0){ + gaps_filled = [undefined, undefined]; + new_ranges = [[undefined, undefined]]; + } else if (ranges.length === 0){ + gaps_filled = [new_range]; + new_ranges = [new_range] + } else { + var covered = false, + last_start = start, + last_end = start; + + ranges.forEach((range, i)=>{ + if (covered){ new_ranges.push(range); return true; } + if (DateUtil.lte(start, range[0])){ + if (end && !DateUtil.eq(end, range[0]) && DateUtil.lte(end, range[0])){ + new_ranges.push([last_start, end]); + new_ranges.push(range); + gaps_filled.push([last_end, end]); + covered = true; + } else if (end && !DateUtil.gte(end, range[1])) { + new_ranges.push([last_start, range[1]]); + if (range[0] && !DateUtil.eq(last_end, range[0])){ gaps_filled.push([last_end, range[0]]); } + covered = true + } else { + if (range[0] && !DateUtil.eq(last_end, range[0])) gaps_filled.push([last_end, range[0]]); + last_end = range[1] + } + } else if (start && DateUtil.gte(range[1], start)){ + if (!DateUtil.eq(end, range[1]) && DateUtil.gte(end, range[1])){ + last_start = range[0]; + last_end = range[1]; + } else { + new_ranges.push(range); + covered = true; + } + } else { new_ranges.push(range); } + }); + if (!covered) { + new_ranges.push([last_start, end]); + if (!DateUtil.eq(last_end, end)) gaps_filled.push([last_end, end]); + } + } + + return { gaps_filled: gaps_filled, new_ranges: new_ranges } + } + + static min(date1, date2){ + + } + + static max(date1, date2){ + + } + + static gte(date1, date2){ + return (date1 === undefined || (date2 !== undefined && date1 >= date2)); + } + + static lte(date1, date2){ + return (date1 === undefined || (date2 !== undefined && date1 <= date2)); + } + + static eq(date1, date2){ + return (date1 !== undefined && date2 !== undefined && date1.getTime() === date2.getTime()) || date1 === undefined && date2 === undefined + } + + static add(date, s){ + return new Date(date.getTime() + s); + } + +} +export default DateUtil; diff --git a/spec/shared/util/date.test.js b/spec/shared/util/date.test.js new file mode 100644 index 0000000..8daa3d9 --- /dev/null +++ b/spec/shared/util/date.test.js @@ -0,0 +1,450 @@ +"use strict"; + +import DateUtil from './../../../shared/utils/date.js'; + +function rangeEquivalent(range1, range2){ + if (range1 && !range2 || !range1 && range2) return false + var equivalent = true + if (range1[0] && range2[0]) { + equivalent = range1[0].getTime() === range2[0].getTime(); + } else { + equivalent = range1[0] === range2[0]; + } + if (!equivalent) return false; + if (range1[1] && range2[1]) { + equivalent = range1[1].getTime() === range2[1].getTime(); + } else { + equivalent = range1[1] === range2[1]; + } + return equivalent +} +function rangesEquivalent(a1, a2){ + var match = true; + if (a1.length !== a2.length) return false; + a1.forEach((range, i)=>{ + if (!rangeEquivalent(range, a2[i])) match = false; return false; + }); + return match; +} + + +describe('DateUtil.gte', ()=>{ + + it('considers undefined as a large date', ()=>{ + var date1 = new Date(), + date2 = new Date(date1.getTime() + 1000); + expect(DateUtil.gte(undefined, date1)).toEqual(true); + expect(DateUtil.gte(undefined, undefined)).toEqual(true); + expect(DateUtil.gte(date1, undefined)).toEqual(false); + expect(DateUtil.gte(date1, date2)).toEqual(false); + expect(DateUtil.gte(date2, date1)).toEqual(true); + }); +}); + + +describe('DateUtil.lte', ()=>{ + it('considers undefined as a small date', ()=>{ + var date1 = new Date(), + date2 = new Date(date1.getTime() + 1000); + expect(DateUtil.lte(undefined, date1)).toEqual(true); + expect(DateUtil.lte(undefined, undefined)).toEqual(true); + expect(DateUtil.lte(date1, undefined)).toEqual(false); + expect(DateUtil.lte(date1, date2)).toEqual(true); + expect(DateUtil.lte(date2, date1)).toEqual(false); + }); +}); + +describe('DateUtil.addRange', ()=>{ + var date1 = new Date(), + date01 = DateUtil.add(date1, -1000), + date11 = DateUtil.add(date1, 1000), + date2 = DateUtil.add(date1, 2000), + date21 = DateUtil.add(date2, 1000), + date3 = DateUtil.add(date2, 2000), + date31 = DateUtil.add(date3, 1000), + date4 = DateUtil.add(date3, 2000), + date41 = DateUtil.add(date4, 1000), + date5 = DateUtil.add(date4, 2000), + date51 = DateUtil.add(date5, 1000), + date6 = DateUtil.add(date5, 2000), + date61 = DateUtil.add(date6, 1000), + date7 = DateUtil.add(date6, 2000), + date71 = DateUtil.add(date7, 1000); + + describe('no ranges exist', ()=>{ + it('returns the new ranges', ()=>{ + var result = DateUtil.addRange([date1, date2], []); + expect(rangesEquivalent(result.gaps_filled, [[date1, date2]])).toBeTruthy(); + expect(rangesEquivalent(result.new_ranges, [[date1, date2]])).toBeTruthy(); + }); + }); + + describe('infinite range exists', ()=>{ + it('returns the infinite range, no gaps filled', ()=>{ + var result = DateUtil.addRange([date1, date2], [[undefined, undefined]]); + expect(rangesEquivalent(result.gaps_filled, [])).toBeTruthy(); + expect(rangesEquivalent(result.new_ranges, [[undefined, undefined]])).toBeTruthy(); + }); + }); + + describe('-Infinity to definite date exists', ()=>{ + describe('with gaps', ()=>{ + var ranges = [[undefined, date1], [date2, date3], [date4, date5]]; + + describe('new range low low', ()=>{ + var new_range = [undefined, date01]; + it('no gaps filled', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]) + expect(result.new_ranges).toEqual(ranges); + }); + }); + + describe('new range low mid', ()=>{ + var new_range = [undefined, date31]; + it('fills mid gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date1, date2], [date3, date31]]); + expect(result.new_ranges).toEqual([[undefined, date31], [date4, date5]]); + }); + }); + + describe('new range low high', ()=>{ + var new_range = [undefined, date61]; + it('fills mid and high gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date1, date2], [date3, date4], [date5, date61]]); + expect(result.new_ranges).toEqual([[undefined, date61]]); + }); + }); + + + describe('new range mid mid', ()=>{ + var new_range = [date11, date41]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date11, date2], [date3, date4]]) + expect(result.new_ranges).toEqual([[undefined, date1], [date11, date5]]); + }); + }); + + describe('new range mid high', ()=>{ + var new_range = [date11, date61]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date11, date2], [date3, date4], [date5, date61]]); + expect(result.new_ranges).toEqual([[undefined, date1], [date11, date61]]); + }); + }); + + describe('new range high high', ()=>{ + var new_range = [date5, date61]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date5, date61]]); + expect(result.new_ranges).toEqual([[undefined, date1], [date2, date3], [date4, date61]]); + }); + }); + + }); + + describe('no gaps', ()=>{ + var ranges = [[undefined, date1]]; + + describe('new range low low', ()=>{ + var new_range = [undefined, date01]; + it('no gaps filled', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]) + expect(result.new_ranges).toEqual([[undefined, date1]]); + }); + }); + + describe('new range low mid', ()=>{ + var new_range = [undefined, date1]; + it('fills mid gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]); + expect(result.new_ranges).toEqual([[undefined, date1]]); + }); + }); + + describe('new range low high', ()=>{ + var new_range = [undefined, date61]; + it('fills mid and high gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date1, date61]]); + expect(result.new_ranges).toEqual([[undefined, date61]]); + }); + }); + + + describe('new range mid mid', ()=>{ + var new_range = [date01, date1]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]) + expect(result.new_ranges).toEqual([[undefined, date1]]); + }); + }); + + describe('new range mid high', ()=>{ + var new_range = [date1, date61]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date1, date61]]); + expect(result.new_ranges).toEqual([[undefined, date61]]); + }); + }); + + describe('new range high high', ()=>{ + var new_range = [date5, date61]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date5, date61]]); + expect(result.new_ranges).toEqual([[undefined, date1], [date5, date61]]); + }); + }); + }); + }); + + describe('definite to Infinity range exists', ()=>{ + describe('with gaps', ()=>{ + var ranges = [[date1, date2], [date3, date4], [date5, undefined]]; + + describe('new range low low', ()=>{ + var new_range = [undefined, date01]; + it('no gaps filled', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date01]]); + expect(result.new_ranges).toEqual([[undefined, date01], [date1, date2], [date3, date4], [date5, undefined]]); + }); + }); + + describe('new range low mid', ()=>{ + var new_range = [undefined, date3]; + it('fills mid gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date1], [date2, date3]]); + expect(result.new_ranges).toEqual([[undefined, date4], [date5, undefined]]); + }); + }); + + describe('new range low high', ()=>{ + var new_range = [undefined, date61]; + it('fills mid and high gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date1], [date2, date3], [date4, date5]]); + expect(result.new_ranges).toEqual([[undefined, undefined]]); + }); + }); + + describe('new range mid mid', ()=>{ + var new_range = [date1, date41]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date2, date3], [date4, date41]]); + expect(result.new_ranges).toEqual([[date1, date41], [date5, undefined]]); + }); + }); + + describe('new range mid high', ()=>{ + var new_range = [date31, date61]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date4, date5]]); + expect(result.new_ranges).toEqual([[date1, date2], [date3, undefined]]); + }); + }); + + describe('new range high high', ()=>{ + var new_range = [date5, date61]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]); + expect(result.new_ranges).toEqual([[date1, date2], [date3, date4], [date5, undefined]]); + }); + }); + }); + + describe('no gaps', ()=>{ + var ranges = [[date1, undefined]]; + + describe('new range low low', ()=>{ + var new_range = [undefined, date01]; + it('no gaps filled', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date01]]); + expect(result.new_ranges).toEqual([[undefined, date01], [date1, undefined]]); + }); + }); + + describe('new range low mid', ()=>{ + var new_range = [undefined, date1]; + it('fills mid gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date1]]); + expect(result.new_ranges).toEqual([[undefined, undefined]]); + }); + }); + + describe('new range low high', ()=>{ + var new_range = [undefined, date61]; + it('fills mid and high gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date1]]); + expect(result.new_ranges).toEqual([[undefined, undefined]]); + }); + }); + + describe('new range mid mid', ()=>{ + var new_range = [date1, date1]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]); + expect(result.new_ranges).toEqual([[date1, undefined]]); + }); + }); + + describe('new range mid high', ()=>{ + var new_range = [date1, undefined]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]); + expect(result.new_ranges).toEqual([[date1, undefined]]); + }); + }); + + describe('new range high high', ()=>{ + var new_range = [date5, undefined]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]); + expect(result.new_ranges).toEqual([[date1, undefined]]); + }); + }); + + }); + }); + + describe('definite range exists', ()=>{ + describe('with gaps', ()=>{ + var ranges = [[date1, date2], [date3, date4], [date5, date6]]; + + describe('new range low low', ()=>{ + var new_range = [undefined, date01]; + it('no gaps filled', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date01]]); + expect(result.new_ranges).toEqual([[undefined, date01], [date1, date2], [date3, date4], [date5, date6]]); + }); + }); + + describe('new range low mid', ()=>{ + var new_range = [undefined, date41]; + it('fills mid gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date1], [date2, date3], [date4, date41]]); + expect(result.new_ranges).toEqual([[undefined, date41], [date5, date6]]); + }); + }); + + describe('new range low high', ()=>{ + var new_range = [undefined, date61]; + it('fills mid and high gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date1], [date2, date3], [date4, date5], [date6, date61]]); + expect(result.new_ranges).toEqual([[undefined, date61]]); + }); + }); + + describe('new range mid mid', ()=>{ + var new_range = [date11, date41]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date2, date3], [date4, date41]]); + expect(result.new_ranges).toEqual([[date1, date41], [date5, date6]]); + }); + }); + + describe('new range mid high', ()=>{ + var new_range = [date31, undefined]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date4, date5], [date6, undefined]]); + expect(result.new_ranges).toEqual([[date1, date2], [date3, undefined]]); + }); + }); + + describe('new range high high', ()=>{ + var new_range = [date61, undefined]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date61, undefined]]); + expect(result.new_ranges).toEqual([[date1, date2], [date3, date4], [date5, date6], [date61, undefined]]); + }); + }); + + }); + + describe('no gaps', ()=>{ + var ranges = [[date1, date2]]; + + describe('new range low low', ()=>{ + var new_range = [undefined, date01]; + it('no gaps filled', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date01]]); + expect(result.new_ranges).toEqual([[undefined, date01], [date1, date2]]); + }); + }); + + describe('new range low mid', ()=>{ + var new_range = [undefined, date11]; + it('fills mid gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date1]]); + expect(result.new_ranges).toEqual([[undefined, date2]]); + }); + }); + + describe('new range low high', ()=>{ + var new_range = [undefined, date61]; + it('fills mid and high gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[undefined, date1], [date2, date61]]); + expect(result.new_ranges).toEqual([[undefined, date61]]); + }); + }); + + describe('new range mid mid', ()=>{ + var new_range = [date11, date2]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([]); + expect(result.new_ranges).toEqual([[date1, date2]]); + }); + }); + + describe('new range mid high', ()=>{ + var new_range = [date11, undefined]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date2, undefined]]); + expect(result.new_ranges).toEqual([[date1, undefined]]); + }); + }); + + describe('new range high high', ()=>{ + var new_range = [date61, undefined]; + it('includes gaps', ()=>{ + var result = DateUtil.addRange(new_range, ranges); + expect(result.gaps_filled).toEqual([[date61, undefined]]); + expect(result.new_ranges).toEqual([[date1, date2], [date61, undefined]]); + }); + }); + + }); + }); + +}); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..bcaf67b --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,12 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "**/*.test.js" + ], + "helpers": [ + "../node_modules/babel-core/register.js", + "helpers/**/*.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +}