diff --git a/client/api/energy_data.js b/client/api/energy_data.js
index 871d581..4da4cd5 100644
--- a/client/api/energy_data.js
+++ b/client/api/energy_data.js
@@ -1,18 +1,25 @@
-const ENDPOINT = '/data/v1/energy';
import extend from 'extend';
+const ENDPOINT = '/data/v1/energy';
+
+// send all date parameters as unix timestamps;
class EnergyDataApi {
static index(params){
params = extend({
-
}, params);
+ if (params.dates){
+ params.dates = params.dates.map((date_range)=>{
+ if (date_range[0]) date_range[0] = date_range[0].unix();
+ if (date_range[1]) date_range[1] = date_range[1].unix();
+ return [date_range[0], date_range[1]];
+ })
+ }
return jQuery.ajax({
- url: `${ENDPOINT}`,
+ url: ENDPOINT + '?' + jQuery.param(params),
type: 'GET',
- params: params,
dataType: 'json'
- }).success((res)=>{
+ }).then((res)=>{
return res.data;
});
}
diff --git a/client/api/houses.js b/client/api/houses.js
index e69de29..ed92db1 100644
--- a/client/api/houses.js
+++ b/client/api/houses.js
@@ -0,0 +1,18 @@
+const ENDPOINT = '/data/v1/houses';
+import extend from 'extend';
+
+class HousesApi {
+
+ static index(params){
+ return jQuery.ajax({
+ url: ENDPOINT + '?' + jQuery.param(params),
+ type: 'GET',
+ dataType: 'json'
+ }).then((res)=>{
+ return res.data;
+ });
+ }
+
+}
+
+export default HousesApi;
diff --git a/client/api/power_data.js b/client/api/power_data.js
index 8b13789..f2aea90 100644
--- a/client/api/power_data.js
+++ b/client/api/power_data.js
@@ -1 +1,29 @@
+const ENDPOINT = '/data/v1/power';
+import extend from 'extend';
+
+// send all date parameters as unix timestamps;
+class PowerDataApi {
+
+ static index(params){
+ params = extend({
+ }, params);
+ if (params.dates){
+ params.dates = params.dates.map((date_range)=>{
+ if (date_range[0]) date_range[0] = date_range[0].unix();
+ if (date_range[1]) date_range[1] = date_range[1].unix();
+ return [date_range[0], date_range[1]];
+ })
+ }
+ return jQuery.ajax({
+ url: ENDPOINT + '?' + jQuery.param(params),
+ type: 'GET',
+ dataType: 'json'
+ }).then((res)=>{
+ return res.data;
+ });
+ }
+
+}
+
+export default PowerDataApi;
diff --git a/client/app.js b/client/app.js
index 2b52233..c6f0749 100644
--- a/client/app.js
+++ b/client/app.js
@@ -1,9 +1,11 @@
import 'babel-polyfill';
+import 'bootstrap/dist/js/bootstrap.min';
import React from 'react';
import ReactDOM from 'react-dom';
import Layout from './dashboard/layout/layout';
+
ReactDOM.render(
React.createElement(Layout),
document.getElementById('root')
diff --git a/client/config/api.js b/client/config/api.js
new file mode 100644
index 0000000..cc0bafa
--- /dev/null
+++ b/client/config/api.js
@@ -0,0 +1 @@
+api.js
diff --git a/client/config/store.js b/client/config/store.js
new file mode 100644
index 0000000..b1c4591
--- /dev/null
+++ b/client/config/store.js
@@ -0,0 +1 @@
+store.js
diff --git a/client/dashboard/layout/layout.js b/client/dashboard/layout/layout.js
index 050df02..09c6871 100644
--- a/client/dashboard/layout/layout.js
+++ b/client/dashboard/layout/layout.js
@@ -1,11 +1,21 @@
import React from 'react';
-
import layoutRt from './layout.rt.js';
+import House from './../../models/house';
+
+const VIEWS = [['power', 'Power Savings'], ['energy', 'Energy Production']];
var Layout = React.createClass({
getInitialState: function(){
- return {view: "????"};
+ var layout = this;
+ layout.view_name = VIEWS[0][1];
+ return {
+ views: VIEWS,
+ houses: null,
+ house: null,
+ view: 'power',
+ requesting_data: true
+ };
},
handleResize: function(e) {
@@ -13,13 +23,49 @@ var Layout = React.createClass({
},
componentDidMount: function() {
- window.addEventListener('resize', this.handleResize);
+ var layout = this;
+ // window.addEventListener('resize', this.handleResize);
+ House.ensureHouses().then((houses)=>{
+ layout.setState({houses: houses, house: houses[0]});
+ layout.ensureHouseViewData();
+ });
},
- setView: function(event) {
- var layout = this;
- console.log(event.target.value)
- layout.setState({view: event.target.value});
+ setView: function(event){
+ var layout = this,
+ view = event.target.value;
+ layout.view_name = event.target.innerText;
+ layout.setState({view: view});
+ layout.ensureHouseViewData();
+ },
+
+ setHouse: function(event){
+ var layout = this,
+ house_id = event.target.value,
+ house = layout.state.houses.find((house)=>{ return house.data.id == house_id });
+ layout.setState({house: house}, function(){
+ layout.ensureHouseViewData();
+ });
+ },
+
+ ensureHouseViewData: function(){
+ var layout = this,
+ house = layout.state.house,
+ view = layout.state.view,
+ request;
+ layout.setState({requesting_data: true}, ()=>{
+ if (view === 'power'){
+ request = house.ensurePowerData();
+ } else {
+ request = house.ensureEnergyData();
+ }
+ request.then(()=>{
+ console.log('data retrieved')
+ layout.setState({requesting_data: false}, ()=>{
+ console.log(layout.state.requesting_data);
+ });
+ });
+ });
},
render: function() {
diff --git a/client/dashboard/layout/layout.rt b/client/dashboard/layout/layout.rt
index fed8186..6bc107d 100644
--- a/client/dashboard/layout/layout.rt
+++ b/client/dashboard/layout/layout.rt
@@ -1,8 +1,54 @@
-
-
{this.state.view}
-
+
Loading data...
+
{this.state.house.name}
+
{this.view_name}
+
+
+
+
+
+
+
+
+
+
+
+ |
+ Day |
+ Consumption (Wh) |
+ Production (Wh) |
+
+
+
+
+ |
+ {energy_datum.day_to_s} |
+ {energy_datum.consumption_to_s} |
+ {energy_datum.production_to_s} |
+
+
+
+
+
+
+
+ |
+ Time |
+ Consumption (W) |
+ Production (W) |
+
+
+
+
+ |
+ {power_datum.time_to_s} |
+ {power_datum.consumption_to_s} |
+ {power_datum.production_to_s} |
+
+
+
diff --git a/client/dashboard/layout/layout.rt.js b/client/dashboard/layout/layout.rt.js
index 67f1406..db1a687 100644
--- a/client/dashboard/layout/layout.rt.js
+++ b/client/dashboard/layout/layout.rt.js
@@ -1,5 +1,45 @@
import React from 'react';
import _ from 'lodash';
+function repeatView1(view, viewIndex) {
+ return React.createElement('option', {
+ 'value': view[0],
+ 'key': 'view-' + view[0]
+ }, view[1]);
+}
+function repeatHouse2(house, houseIndex) {
+ return React.createElement('option', {
+ 'value': house.data.id,
+ 'key': house.react_key
+ }, house.data.name);
+}
+function repeatEnergy_datum3(energy_datum, energy_datumIndex) {
+ return React.createElement('tr', { 'key': energy_datum.react_key }, React.createElement('td', {}), React.createElement('td', {}, energy_datum.day_to_s), React.createElement('td', {}, energy_datum.consumption_to_s), React.createElement('td', {}, energy_datum.production_to_s));
+}
+function repeatPower_datum4(power_datum, power_datumIndex) {
+ return React.createElement('tr', { 'key': power_datum.react_key }, React.createElement('td', {}), React.createElement('td', {}, power_datum.time_to_s), React.createElement('td', {}, power_datum.consumption_to_s), React.createElement('td', {}, power_datum.production_to_s));
+}
export default function () {
- return React.createElement('div', { 'id': 'layout' }, React.createElement('h1', {}, this.state.view), React.createElement('select', { 'onChange': this.setView }, React.createElement('option', { 'value': 'savings' }, 'Savings'), React.createElement('option', { 'value': 'production' }, 'Production')));
+ return React.createElement('div', { 'id': 'layout' }, this.state.requesting_data ? React.createElement('div', { 'className': 'alert alert-warning' }, 'Loading data...') : null, this.state.house ? React.createElement('h1', {}, this.state.house.name) : null, this.state.view ? React.createElement('h3', {}, this.view_name) : null, React.createElement('div', {}, React.createElement.apply(this, [
+ 'select',
+ {
+ 'className': 'form-control',
+ 'onChange': this.setView
+ },
+ _.map(this.state.views, repeatView1.bind(this))
+ ])), this.state.houses ? React.createElement('div', {}, React.createElement.apply(this, [
+ 'select',
+ {
+ 'className': 'form-control',
+ 'onChange': this.setHouse
+ },
+ _.map(this.state.houses, repeatHouse2.bind(this))
+ ])) : null, this.state.view === 'energy' && this.state.house ? React.createElement('table', { 'className': 'table' }, React.createElement('thead', {}, React.createElement('tr', {}, React.createElement('th', {}), React.createElement('th', {}, 'Day'), React.createElement('th', {}, 'Consumption (Wh)'), React.createElement('th', {}, 'Production (Wh)'))), React.createElement.apply(this, [
+ 'tbody',
+ {},
+ _.map(this.state.house.energy_data, repeatEnergy_datum3.bind(this))
+ ])) : null, this.state.view === 'power' && this.state.house ? React.createElement('table', { 'className': 'table' }, React.createElement('thead', {}, React.createElement('tr', {}, React.createElement('th', {}), React.createElement('th', {}, 'Time'), React.createElement('th', {}, 'Consumption (W)'), React.createElement('th', {}, 'Production (W)'))), React.createElement.apply(this, [
+ 'tbody',
+ {},
+ _.map(this.state.house.power_data, repeatPower_datum4.bind(this))
+ ])) : null);
};
\ No newline at end of file
diff --git a/client/models/energy_datum.js b/client/models/energy_datum.js
index a3d58ec..25c37d1 100644
--- a/client/models/energy_datum.js
+++ b/client/models/energy_datum.js
@@ -1,13 +1,17 @@
import extend from 'extend';
-
+import moment from 'moment-timezone';
class EnergyDatum {
- __constructor(data, house){
+ constructor(data, house){
var energy_datum = this;
energy_datum.house = house;
+ data.day = moment.tz(data.day, house.data.timezone);
energy_datum.data = data;
- moment.tz(data.day, house.data.timezone);
- EnergyDatum.store[data.id] energy_datum;
+ EnergyDatum.store.set(data.id, energy_datum);
+ }
+
+ get react_key(){
+ return `energy-datum-${this.data.id}`;
}
get day_to_date(){
@@ -17,6 +21,21 @@ class EnergyDatum {
return moment(moment_tz.toArray()).toDate();
}
+ get day_to_s(){
+ var energy_datum = this;
+ return energy_datum.data.day.format('YYYY-MM-DD');
+ }
+
+ get consumption_to_s(){
+ var energy_datum = this;
+ return Math.round(energy_datum.data.consumption);
+ }
+
+ get production_to_s(){
+ var energy_datum = this;
+ return Math.round(energy_datum.data.production);
+ }
+
update(data){
var energy_datum = this,
house = power_datum.house;
@@ -24,11 +43,13 @@ class EnergyDatum {
extend(energy_datum.data, data);
}
- static updateOrInitialize(id, data, house){
- var energy_datum = EnergyDatum.store.get(id);
+ static updateOrInitialize(data, house){
+ var energy_datum = EnergyDatum.store.get(data.id);
if (energy_datum) energy_datum.update(data);
return energy_datum || new EnergyDatum(data, house)
}
}
EnergyDatum.store = new Map();
+
+export default EnergyDatum;
diff --git a/client/models/house.js b/client/models/house.js
index d8a3499..730e612 100644
--- a/client/models/house.js
+++ b/client/models/house.js
@@ -1,38 +1,56 @@
-import Api from './../api';
-import Store from './../store';
-import ArrayUtil from './../../shared/util/array'
+import extend from 'extend';
-class House extends Base {
+import PowerDatum from './power_datum';
+import EnergyDatum from './energy_datum';
+import PowerDataApi from './../api/power_data';
+import EnergyDataApi from './../api/energy_data';
+import HousesApi from './../api/houses';
+import ArrayUtil from './../../shared/utils/array'
+import MathUtil from './../../shared/utils/math'
- __constructor(data, house){
- var energy_datum = this;
+class House {
+
+ constructor(data){
+ var house = this;
House.store.set(data.id, house);
house.data = data;
- house.energy_data = new Map();
- house.power_data = new Map();
+ house.energy_data = [];
+ house.power_data = [];
+ house.energy_data_store = new Map();
+ house.power_data_store = new Map();
}
- ensurePowerData(start_date, end_date){
+ get react_key(){
+ return `house-${this.data.id}`;
+ }
+
+ ensurePowerData(opts){
+ opts = extend({
+ start_date: undefined,
+ end_date: undefined
+ }, opts || {});
var house = this,
- date_range = Array.from(house.power_data.keys()),
+ date_range = Array.from(house.power_data_store.keys()),
min_date = Math.min(date_range),
max_date = Math.max(date_range),
query_ranges, cache;
- if (date_range.length === 0) return house.getPowerData({dates: [[start_date, end_date]]})
+ if (date_range.length === 0){
+ return house.getPowerData({dates: [[opts.start_date, opts.end_date]]});
+ }
- query_ranges = MathUtil.minusRange([start_date, end_date], [min_date, max_date]);
+ query_ranges = MathUtil.minusRange([opts.start_date, opts.end_date], [min_date, max_date]);
cache = ArrayUtil.selectMap(date_range, (datum_time)=>{
return ArrayUtil.all(query_ranges, (query_range)=>{
!MathUtil.inRange(datum_time, query_range);
- }));
+ });
}, (datum_time)=>{
- return house.power_data.get(datum_time);
+ return house.power_data_store.get(datum_time);
});
if (query_ranges.length > 0){
- return house.getPowerData({dates: dates}).then((new_power_data)=>{
+ return house.getPowerData({dates: query_ranges}).then((new_power_data)=>{
return new_power_data.concat(cache);
});
} else return Promise.resolve(cache);
@@ -41,37 +59,42 @@ class House extends Base {
getPowerData(params){
var house = this;
params.house_id = house.data.id;
- return Api.PowerData.index(params).then((power_data)=>{
+ return PowerDataApi.index(params).then((power_data)=>{
return power_data.map((power_datum_data)=>{
- var power_datum = Store.PowerDatum.updateOrInitialize(power_datum_data, house);
- house.power_data.set(power_datum.data.time, power_datum);
+ var power_datum = PowerDatum.updateOrInitialize(power_datum_data, house);
+ house.power_data_store.set(power_datum.data.time, power_datum);
+ house.power_data.push(power_datum);
return power_datum;
});
});
}
- ensureEnergyData(start_date, end_date){
+ ensureEnergyData(opts){
+ opts = extend({
+ start_date: undefined,
+ end_date: undefined
+ }, opts || {});
var house = this,
- date_range = Array.from(house.energy_data.keys()),
+ date_range = Array.from(house.energy_data_store.keys()),
min_date = Math.min(date_range),
max_date = Math.max(date_range),
query_ranges, cache;
- if (date_range.length === 0) return house.getEnergyData({dates: [[start_date, end_date]]})
+ if (date_range.length === 0) return house.getEnergyData({dates: [[opts.start_date, opts.end_date]]})
query_ranges = MathUtil.minusRange([start_date, end_date], [min_date, max_date]);
cache = ArrayUtil.selectMap(date_range, (datum_day)=>{
return ArrayUtil.all(query_ranges, (query_range)=>{
- !MathUtil.inRange(datum_day, query_range);
- }));
+ return !MathUtil.inRange(datum_day, query_range);
+ });
}, (datum_day)=>{
- return house.energy_data.get(datum_day);
+ return house.energy_data_store.get(datum_day);
});
if (query_ranges.length > 0){
- return house.getEnergyData({dates: dates}).then((new_energy_data)=>{
- return new_energy_data.concat(cache);
+ return house.getEnergyData({dates: query_ranges}).then((new_energy_data)=>{
+ return new_energy_data_store.concat(cache);
});
} else return Promise.resolve(cache);
}
@@ -79,10 +102,11 @@ class House extends Base {
getEnergyData(params){
var house = this;
params.house_id = house.data.id;
- return Api.PowerData.index(params).then((energy_data)=>{
- return power_data.map((energy_datum_data)=>{
- var energy_datum = Store.EnergyDatum.updateOrInitialize(energy_datum_data, house);
- house.energy_data.set(power_datum.time, energy_datum);
+ return EnergyDataApi.index(params).then((energy_data)=>{
+ return energy_data.map((energy_datum_data)=>{
+ var energy_datum = EnergyDatum.updateOrInitialize(energy_datum_data, house);
+ house.energy_data_store.set(power_datum.time, energy_datum);
+ house.energy_data.push(energy_datum);
return energy_datum;
});
});
@@ -100,18 +124,21 @@ class House extends Base {
}
static ensureHouses(ids){
- var required_ids = ArrayUtil.diff(ids, House.store.keys()),
+ var required_ids, cached_houses = [];
+ if (ids){
+ required_ids = ArrayUtil.diff(ids, House.store.keys());
cached_houses = ArrayUtil.diff(ids, required_ids).map((id)=>{ return House.store.get(id); });
- if (required_ids.length == 0) return Promise.resolve([]);
+ }
+ if (required_ids && required_ids.length == 0) return Promise.resolve([]);
- return House.getHouses(required_ids).then((new_houses){
+ return House.getHouses(required_ids).then((new_houses)=>{
// if these need to be ordered, then concatenation needs to be merged in order.
return new_houses.concat(cached_houses);
});
}
static getHouses(ids){
- return Api.HousesApi.index({id: ids}).then((houses_data)=>{
+ return HousesApi.index({id: ids}).then((houses_data)=>{
return houses_data.map((house_data)=>{
return new House(house_data);
});
diff --git a/client/models/power_datum.js b/client/models/power_datum.js
index bd4ac26..22b3c88 100644
--- a/client/models/power_datum.js
+++ b/client/models/power_datum.js
@@ -1,13 +1,17 @@
import extend from 'extend';
-
+import moment from 'moment-timezone';
class PowerDatum {
- __constructor(data, house){
+ constructor(data, house){
var power_datum = this;
power_datum.house = house;
+ data.time = moment.tz(data.time, house.data.timezone);
power_datum.data = data;
- moment.format(data.time);
- PowerDatum.store[data.id] power_datum;
+ PowerDatum.store.set(data.id, power_datum);
+ }
+
+ get react_key(){
+ return `power-datum-${this.data.id}`;
}
get time_to_date(){
@@ -17,6 +21,19 @@ class PowerDatum {
return moment(moment_tz.toArray()).toDate();
}
+ get time_to_s(){
+ var power_datum = this;
+ return power_datum.data.time.format('YYYY-MM-DD HH:MM');
+ }
+ get consumption_to_s(){
+ var power_datum = this;
+ return Math.round(power_datum.data.consumption);
+ }
+ get production_to_s(){
+ var power_datum = this;
+ return Math.round(power_datum.data.production);
+ }
+
update(data){
var power_datum = this,
house = power_datum.house;
@@ -24,8 +41,8 @@ class PowerDatum {
extend(power_datum.data, data);
}
- static updateOrInitialize(id, data, house){
- var power_datum = PowerDatum.store.get(id);
+ static updateOrInitialize(data, house){
+ var power_datum = PowerDatum.store.get(data.id);
if (power_datum) power_datum.update(data);
return power_datum || new PowerDatum(data, house)
}
@@ -33,3 +50,5 @@ class PowerDatum {
}
PowerDatum.store = new Map();
+
+export default PowerDatum;
diff --git a/npm-debug.log b/npm-debug.log
deleted file mode 100644
index f818a33..0000000
--- a/npm-debug.log
+++ /dev/null
@@ -1,45 +0,0 @@
-0 info it worked if it ends with ok
-1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'start' ]
-2 info using npm@3.5.3
-3 info using node@v5.4.1
-4 verbose run-script [ 'prestart', 'start', 'poststart' ]
-5 info lifecycle spike_proto@0.0.0~prestart: spike_proto@0.0.0
-6 silly lifecycle spike_proto@0.0.0~prestart: no script for prestart, continuing
-7 info lifecycle spike_proto@0.0.0~start: spike_proto@0.0.0
-8 verbose lifecycle spike_proto@0.0.0~start: unsafe-perm in lifecycle true
-9 verbose lifecycle spike_proto@0.0.0~start: PATH: /usr/local/lib/node_modules/npm/bin/node-gyp-bin:/home/eric/Code/spike2/node_modules/.bin:/home/eric/.rvm/gems/ruby-1.9.3-p484@oroeco_dev/bin:/home/eric/.rvm/gems/ruby-1.9.3-p484@global/bin:/home/eric/.rvm/rubies/ruby-1.9.3-p484/bin:/home/eric/.rvm/bin:/home/eric/bin:/usr/local/heroku/bin:/home/eric/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/lampp/bin
-10 verbose lifecycle spike_proto@0.0.0~start: CWD: /home/eric/Code/spike2
-11 silly lifecycle spike_proto@0.0.0~start: Args: [ '-c', 'babel-node ./server/app.express.js' ]
-12 silly lifecycle spike_proto@0.0.0~start: Returned: code: 1 signal: null
-13 info lifecycle spike_proto@0.0.0~start: Failed to exec start script
-14 verbose stack Error: spike_proto@0.0.0 start: `babel-node ./server/app.express.js`
-14 verbose stack Exit status 1
-14 verbose stack at EventEmitter. (/usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:232:16)
-14 verbose stack at emitTwo (events.js:87:13)
-14 verbose stack at EventEmitter.emit (events.js:172:7)
-14 verbose stack at ChildProcess. (/usr/local/lib/node_modules/npm/lib/utils/spawn.js:24:14)
-14 verbose stack at emitTwo (events.js:87:13)
-14 verbose stack at ChildProcess.emit (events.js:172:7)
-14 verbose stack at maybeClose (internal/child_process.js:821:16)
-14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)
-15 verbose pkgid spike_proto@0.0.0
-16 verbose cwd /home/eric/Code/spike2
-17 error Linux 3.19.0-49-generic
-18 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "start"
-19 error node v5.4.1
-20 error npm v3.5.3
-21 error code ELIFECYCLE
-22 error spike_proto@0.0.0 start: `babel-node ./server/app.express.js`
-22 error Exit status 1
-23 error Failed at the spike_proto@0.0.0 start script 'babel-node ./server/app.express.js'.
-23 error Make sure you have the latest version of node.js and npm installed.
-23 error If you do, this is most likely a problem with the spike_proto package,
-23 error not with npm itself.
-23 error Tell the author that this fails on your system:
-23 error babel-node ./server/app.express.js
-23 error You can get information on how to open an issue for this project with:
-23 error npm bugs spike_proto
-23 error Or if that isn't available, you can get their info via:
-23 error npm owner ls spike_proto
-23 error There is likely additional logging output above.
-24 verbose exit [ 1, true ]
diff --git a/package.json b/package.json
index cf47781..7105210 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
"raw-loader": "0.5.1",
"sass-loader": "3.1.2",
"style-loader": "^0.12.3",
+ "json-loader": "0.5.4",
"node-sass": "3.4.2",
"moment-timezone":"0.5.0",
"yargs": "3.32.0",
diff --git a/server/app.express.js b/server/app.express.js
index 2726f4a..ff46718 100644
--- a/server/app.express.js
+++ b/server/app.express.js
@@ -45,7 +45,7 @@ var config = require('./config/webpack/development'),
contentBase: __dirname + '/../client/build/development',
publicPath: "/assets/",
proxy: {
- '/data': `http://localhost:${APP_PORT}`,
+ '/data*': `http://localhost:${API_PORT}`,
},
stats: {colors: true}
}),
diff --git a/server/config/webpack/development.js b/server/config/webpack/development.js
index 5b3ce84..aa8502b 100644
--- a/server/config/webpack/development.js
+++ b/server/config/webpack/development.js
@@ -11,10 +11,6 @@ module.exports = {
filename: '[name].js',
path: ROOT + 'client/build/development'
},
- externals: {
- jquery: "$",
- d3: "d3"
- },
module: {
loaders: [
{
@@ -26,6 +22,9 @@ module.exports = {
}, {
test: /\.js$/,
loader: 'babel'
+ }, {
+ test: /\.json$/,
+ loader: 'json-loader'
}
]
},
@@ -37,6 +36,10 @@ module.exports = {
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
+ }),
+ new webpack.ProvidePlugin({
+ d3: "d3",
+ "window.d3": "d3"
})
]
}
diff --git a/server/controllers/energy_controller.js b/server/controllers/energy_controller.js
index e3bc2c9..06c05d0 100644
--- a/server/controllers/energy_controller.js
+++ b/server/controllers/energy_controller.js
@@ -5,10 +5,8 @@ const NAME = 'EnergyController';
class EnergyController{
static index(req, res){
- DB.House.findOne({where: {name: req.housename}}).then((house)=>{
- house.getEnergyDataByTime(req.params.start_time, req.params.end_time).then((energy_data)=>{
- req.json(energy_data);
- });
+ DB.EnergyDatum.exposeForHouseAtDates(req.query.house_id, req.query.dates).then((energy_data)=>{
+ req.json({data: energy_data});
});
}
diff --git a/server/controllers/houses_controller.js b/server/controllers/houses_controller.js
index 7a14c68..9b4c328 100644
--- a/server/controllers/houses_controller.js
+++ b/server/controllers/houses_controller.js
@@ -1,12 +1,14 @@
import DB from './../config/database.js';
-const NAME = HousesController;
+const NAME = 'HousesController';
-class HousesController{
+class HousesController {
static index(req, res){
- DB.House.findAll({attributes: ['id', 'name']}).then((houses)=>{
- res.json(houses);
+ var params = {};
+ if (req.query.ids) query.id = ids;
+ DB.House.findAll({where: params, attributes: ['id', 'name', 'timezone']}).then((houses)=>{
+ res.json({data: houses.map((house)=>{ return house.dataValues; })});
});
}
diff --git a/server/controllers/power_controller.js b/server/controllers/power_controller.js
index b8f132f..59ca459 100644
--- a/server/controllers/power_controller.js
+++ b/server/controllers/power_controller.js
@@ -5,10 +5,9 @@ const NAME = 'PowerController';
class PowerController{
static index(req, res){
- DB.House.findOne({where: {name: req.housename}}).then((house)=>{
- house.getPowerDataByTime(req.params.start_time, req.params.end_time).then((power_data)=>{
- res.json(power_data);
- });
+ console.log(req.query);
+ DB.PowerDatum.exposeForHouseAtDates(req.query.house_id, req.query.dates).then((power_data)=>{
+ res.json({data: power_data});
});
}
diff --git a/server/helpers/api_helper.js b/server/helpers/api_helper.js
new file mode 100644
index 0000000..a62754a
--- /dev/null
+++ b/server/helpers/api_helper.js
@@ -0,0 +1,31 @@
+import moment from 'moment';
+
+class ApiHelper {
+
+ // assume all dates from api coming as UNIX timestamps.
+ static datesParamToSequelize(dates, field_name){
+ if (!dates) return {};
+ var params = {};
+
+ if (dates.length > 1){
+ params['$or'] = [];
+ dates.forEach((min_max)=>{
+ var condition_n = {};
+ condition_n[field_name] = {};
+ if (min_max[0]) condition_n[field_name]['$gt'] = moment.unix(min_max[0]).toDate();
+ if (min_max[1]) condition_n[field_name]['$lt'] = moment.unix(min_max[1]).toDate();
+ if (Object.keys(condition_n).length) params['$or'].push(condition_n);
+ });
+ } else {
+ var min_max = dates[0],
+ condition = {}
+ if (min_max[0]) params[field_name]['$gt'] = moment.unix(min_max[0]).toDate();
+ if (min_max[1]) params[field_name]['$lt'] = moment.unix(min_max[1]).toDate();
+ if (Object.keys(condition).length) params[field_name] = condition;
+ }
+ return params;
+ }
+
+}
+
+export default ApiHelper;
diff --git a/server/models/energy_datum.js b/server/models/energy_datum.js
index 709e8ff..77cae18 100644
--- a/server/models/energy_datum.js
+++ b/server/models/energy_datum.js
@@ -1,4 +1,6 @@
import DB from "./../config/database";
+import extend from 'extend';
+import ApiHelper from './../helpers/api_helper';
const NAME = 'EnergyDatum';
@@ -20,7 +22,12 @@ var EnergyDatum = DB.sequelize.define(NAME, {
underscored: true,
tableName: "energy_data",
instanceMethods: {
-
+ exposeToApi: ()=>{
+ var energy_datum = this,
+ values = this.dataValues;
+ values.energy_datum = energy_datum.day.getTime() / 1000;
+ return values;
+ }
},
classMethods: {
set: ()=>{
@@ -28,6 +35,18 @@ var EnergyDatum = DB.sequelize.define(NAME, {
},
associate: ()=>{
EnergyDatum.belongsTo(DB.House);
+ },
+ exposeForHouseAtDates: (house_id, dates)=>{
+ var params = {house_id: house_id};
+ extend(params, ApiHelper.datesParamToSequelize(dates, 'day'));
+ return EnergyDatum.findAll({
+ where: params,
+ attributes: ['id', 'production', 'consumption', 'day']
+ }).then((energy_data)=>{
+ return energy_data.map((energy_datum)=>{
+ return energy_datum.exposeToApi();
+ });
+ });
}
}
});
diff --git a/server/models/house.js b/server/models/house.js
index 2084486..ad3d453 100644
--- a/server/models/house.js
+++ b/server/models/house.js
@@ -44,8 +44,6 @@ var House = DB.sequelize.define(NAME, {
power_data.forEach((power_datum)=>{
var day = house.timeToDateString(power_datum.time),
energy_datum = energy_data.get(day) || {production: 0, consumption: 0, day: day, house_id: house.id};
- console.log(power_datum.time)
- console.log(day)
energy_datum.production += power_datum.production;
energy_datum.consumption += power_datum.consumption;
energy_data.set(day, energy_datum);
@@ -62,10 +60,10 @@ var House = DB.sequelize.define(NAME, {
associate: ()=>{
House.hasMany(DB.PowerDatum, {as: 'PowerData'});
},
- getPowerDataByTime: (start_date, end_date)=>{
+ getPowerDataByTime: (dates)=>{
var params = {
where: {time: {}},
- attributes: ['time', 'consumption', 'production']
+ attributes: ['id', 'time', 'consumption', 'production']
};
if (start_date) params.where.time.$gt = moment.utc(start_date).toDate();
if (end_date) params.where.time.$lt = moment.utc(end_date).toDate();
diff --git a/server/models/power_datum.js b/server/models/power_datum.js
index e9fed99..36bc3b2 100644
--- a/server/models/power_datum.js
+++ b/server/models/power_datum.js
@@ -1,4 +1,6 @@
import DB from "./../config/database";
+import extend from 'extend';
+import ApiHelper from './../helpers/api_helper';
const NAME = 'PowerDatum';
@@ -20,9 +22,28 @@ var PowerDatum = DB.sequelize.define(NAME, {
underscored: true,
tableName: "power_data",
instanceMethods: {
-
+ exposeToApi: function(){
+ var power_datum = this,
+ data = power_datum.dataValues;
+ data.consumption = data.consumption * 4; // convert Wh / 15 minutes, to W
+ data.production = data.production * 4; // convert Wh / 15 minutes, to W
+ return data;
+ }
},
classMethods: {
+ exposeForHouseAtDates: (house_id, dates)=>{
+ var params = {house_id: house_id};
+ params = extend(params, ApiHelper.datesParamToSequelize(dates, 'time'));
+ console.log(params);
+ return PowerDatum.findAll({
+ where: params,
+ attributes: ['id', 'production', 'consumption', 'time']
+ }).then((power_data)=>{
+ return power_data.map((power_datum)=>{
+ return power_datum.exposeToApi();
+ });
+ });
+ },
set: ()=>{
PowerDatum.associate();
},
diff --git a/server/routes.js b/server/routes.js
index ae92e45..3502aa6 100644
--- a/server/routes.js
+++ b/server/routes.js
@@ -4,8 +4,8 @@ export default function(app){
Controllers.sync();
- app.use('/data/v1/savings/:housename', Controllers.PowerController.index);
- app.use('/data/v1/production/:housename', Controllers.EnergyController.index);
- app.use('/data/v1/houses/', Controllers.EnergyController.index);
+ app.use('/data/v1/power', Controllers.PowerController.index);
+ app.use('/data/v1/energy', Controllers.EnergyController.index);
+ app.use('/data/v1/houses', Controllers.HousesController.index);
};
diff --git a/shared/utils/array.js b/shared/utils/array.js
index 24dc1a9..bc333bf 100644
--- a/shared/utils/array.js
+++ b/shared/utils/array.js
@@ -1,15 +1,13 @@
class ArrayUtil {
static diff(a1, a2){
- a1.filter((a1n)=>{ return a2.indexOf(a1n) < 0; });
+ return a1.filter((a1n)=>{ return a2.indexOf(a1n) < 0; });
}
static selectMap(a, fnSelect, fnMap){
var map = [];
for (var elem of a){
- if (fnSelect(elem)){
- map.push(fnMap(elem));
- }
+ if (fnSelect(elem)) map.push(fnMap(elem));
}
return map;
}
diff --git a/shared/utils/math.js b/shared/utils/math.js
index 802ee37..ef78d36 100644
--- a/shared/utils/math.js
+++ b/shared/utils/math.js
@@ -26,7 +26,9 @@ export default class {
}
static inRange(n, min_max){
- return n >= min_max[0] && n =< min_max[1];
+ var min = min_max[0],
+ max = min_max[1];
+ return ((n >= min) && (n <= max));
}
}