create irradiance composite graph

This commit is contained in:
Eric Hulburd
2016-03-11 17:38:03 -06:00
parent 2dd9389694
commit 782f5cbf91
32 changed files with 824 additions and 379 deletions

View File

@@ -1,9 +1,6 @@
<rt-require dependency="./graph/graph.component" as="EnergyGraph"/>
<rt-require dependency="./table/table.component" as="EnergyTable"/>
<div id="energy_view">
<div class="alert alert-warning" rt-if="this.loading_energy_data">
Retrieving energy data...
</div>
<div rt-if="this.props.view === 'graph'">
<h4>Select Data</h4>
<div class="btn-group" role="group">

View File

@@ -1,4 +1,5 @@
import React from 'react';
import c3 from 'c3';
import Templates from 'config/templates';
import CalendarGridChart from './../../../d3/grid/calendar_grid';

View File

@@ -4,6 +4,7 @@
<th></th>
<th>Day</th>
<th>Consumption (kWh)</th>
<th>Daily Mean Irradiance (W/m<sup>2</sup>)</th>
<th>Production (kWh)</th>
</tr>
</thead>
@@ -11,8 +12,9 @@
<tr rt-repeat="energy_datum in this.house.energy_data" key="{energy_datum.scoped_id}">
<td></td>
<td>{energy_datum.day_to_s}</td>
<td>{energy_datum.consumption_to_s}</td>
<td>{energy_datum.production_to_s}</td>
<td>{energy_datum.consumption}</td>
<td>{energy_datum.irradiance}</td>
<td>{energy_datum.production}</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,152 @@
import React from 'react';
import Templates from 'config/templates';
import c3 from 'c3';
class GraphComponent extends React.Component {
constructor(props){
super(props);
}
get state_manager(){
return this.props.state_manager;
}
get houses(){
return this.state_manager.houses;
}
get chart_data(){
var irradiance_graph = this;
return Object.keys(irradiance_graph.state_manager.irradiance_data).map((day)=>{
var day_data = irradiance_graph.state_manager.irradiance_data[day],
day_datum = {date: day};
day_data.forEach((energy_datum)=>{
day_datum['irradiance'+energy_datum.house.data.id] = energy_datum.irradiance;
day_datum['production'+energy_datum.house.data.id] = energy_datum.production;
});
return day_datum;
}).filter((day_datum)=>{
// due to timezone offsets, some houses might not have an energy_datum point,
// where others do. Just filter those dates out to avoid UI confusion.
return Object.keys(day_datum).length === irradiance_graph.value_keys.length;
});
}
get value_keys(){
var irradiance_graph = this;
return ['date'].concat(Object.keys(irradiance_graph.names));
}
get irradiance_keys(){
return this.houses.map((house)=>{
return 'irradiance' + house.data.id;
});
}
get production_keys(){
return this.houses.map((house)=>{
return 'production' + house.data.id;
});
}
get colors(){
var fnColor = d3.scale.category20(),
irradiance_graph = this;
return Object.keys(irradiance_graph.names).reduce((colors, key)=>{
colors[key] = fnColor(key);
return colors;
}, {});
}
get names(){
var names = {};
this.houses.forEach((house)=>{
names['irradiance' + house.data.id] = house.data.name + ' Irradiance';
names['production' + house.data.id] = house.data.name + ' Production';
});
return names;
}
get axes(){
var irradiance_graph = this,
axes = {};
irradiance_graph.production_keys.forEach((production_key)=>{
axes[production_key] = 'y';
});
irradiance_graph.irradiance_keys.forEach((irradiance_key)=>{
axes[irradiance_key] = 'y2';
});
return axes;
}
get types(){
var irradiance_graph = this;
return irradiance_graph.production_keys.reduce((types, production_key)=>{
types[production_key] = 'bar';
return types;
}, {});
}
componentDidMount(){
var irradiance_graph = this;
irradiance_graph.updateGraph();
}
componentDidUpdate(prev_props, prev_state){
var irradiance_graph = this;
if (irradiance_graph.props.date_interval[0] != prev_props.date_interval[0] ||
irradiance_graph.props.date_interval[1] != prev_props.date_interval[1]){
irradiance_graph.updateGraph();
}
}
updateGraph(){
var irradiance_graph = this,
data = {
json: irradiance_graph.chart_data,
keys: {
x: 'date', // it's possible to specify 'x' when category axis
value: irradiance_graph.value_keys,
},
types: irradiance_graph.types,
names: irradiance_graph.names,
groups: [irradiance_graph.production_keys],
axes: irradiance_graph.axes,
colors: irradiance_graph.colors
};
if (!irradiance_graph.chart){
irradiance_graph.chart = c3.generate({
bindto: '#irradiance_graph',
data: data,
axis: {
x: {
type: 'timeseries',
tick: { format: d3.time.format('%d %B %y') }
},
y: {
label: 'Production'
},
y2: {
show: true,
label: 'Irradiance'
}
}
});
} else {
console.log('reloading data')
console.log(data)
data.unload = irradiance_graph.chart.data;
irradiance_graph.chart.load(data);
}
}
render() {
var irradianceGraphRt = Templates.forComponent('irradiance_graph');
return irradianceGraphRt.call(this);
}
}
GraphComponent.NAME = 'IrradianceGraph';
module.exports = GraphComponent;

View File

@@ -0,0 +1 @@
<div id="irradiance_graph"></div>

View File

@@ -0,0 +1,18 @@
import React from 'react';
import Templates from 'config/templates';
class IrradianceComponent extends React.Component {
get state_manager(){
return this.props.state_manager;
}
render() {
var irradianceRt = Templates.forComponent('irradiance');
return irradianceRt.call(this);
}
}
IrradianceComponent.NAME = 'Irradiance';
module.exports = IrradianceComponent;

View File

@@ -0,0 +1,13 @@
<rt-require dependency="./graph/graph.component" as="IrradianceGraph"/>
<rt-require dependency="./table/table.component" as="IrradianceTable"/>
<div id="irradiance_view">
<h4>Irradiance</h4>
<IrradianceGraph
rt-if="this.props.view === 'graph'"
state_manager="{this.state_manager}"
date_interval="{this.props.date_interval}" />
<IrradianceTable
rt-if="this.props.view === 'table'"
state_manager="{this.state_manager}"
date_interval="{this.props.date_interval}" />
</div>

View File

@@ -0,0 +1,3 @@
#irradiance_component {
}

View File

@@ -0,0 +1,18 @@
import React from 'react';
import Templates from 'config/templates';
class TableComponent extends React.Component {
get state_manager(){
return this.props.state_manager;
}
render() {
var irradianceTableRt = Templates.forComponent('irradiance_table');
return irradianceTableRt.call(this);
}
}
TableComponent.NAME = 'IrradianceTable';
module.exports = TableComponent;

View File

@@ -0,0 +1,18 @@
<table id="irradiance_table" class="table">
<thead>
<tr>
<th>Day</th>
<th>House</th>
<th>Daily Mean Irradiance (W/m<sup>2</sup>)</th>
<th>Production (kWh)</th>
</tr>
</thead>
<tbody rt-repeat="day in Object.keys(this.state_manager.irradiance_data)" key="irradiance-date-{day}">
<tr rt-repeat="energy_datum in this.state_manager.irradiance_data[day]" key="{energy_datum.scoped_id}">
<td>{energy_datum.day_to_s}</td>
<td>{energy_datum.house.data.name}</td>
<td>{energy_datum.irradiance}</td>
<td>{energy_datum.production}</td>
</tr>
</tbody>
</table>

View File

@@ -6,6 +6,7 @@ import Templates from 'config/templates';
import House from './../../models/house';
import PowerDatum from './../../models/power_datum';
import StateManager from './../state_manager';
import DateRangeSlider from './../../d3/sliders/date_range';
class LayoutComponent extends React.Component {
@@ -18,6 +19,8 @@ class LayoutComponent extends React.Component {
house: null,
dataset: null,
year: null,
month: null,
date_interval: null,
view: null
}
}
@@ -50,9 +53,35 @@ class LayoutComponent extends React.Component {
});
}
componentDidUpdate(prev_props, prev_state){
var layout = this;
if (layout.shouldShowDateRange() && !layout.datesMatch(prev_state)){
layout.updateDateRange();
} else if (!layout.shouldShowDateRange()){
layout.destroyDateRange();
}
}
datesMatch(prev_state){
var layout = this;
return layout.state.month == prev_state.month &&
layout.state.year == prev_state.year &&
!layout.shouldShowDateRange() ||
layout.state.date_interval && prev_state.date_interval &&
layout.state.date_interval[0] == prev_state.date_interval[0] &&
layout.state.date_interval[1] == prev_state.date_interval[1];
}
shouldShowDateRange(){
var layout = this;
return layout.state.house && layout.state.dataset === 'power' || layout.state.dataset === 'irradiance';
}
syncFromStateManager(fnStateSet){
var layout = this;
layout.setState(layout.state_manager.state, fnStateSet);
layout.setState(layout.state_manager.state, ()=>{
fnStateSet()
});
}
setHouse(event){
@@ -72,6 +101,50 @@ class LayoutComponent extends React.Component {
layout.state_manager.setParams(update, layout);
}
destroyDateRange(){
var layout = this,
container = document.getElementById('date_interval');
if (container) container.innerHTML = '';
layout.date_interval_slider = undefined;
}
updateDateRange(){
var layout = this,
house = layout.house,
state_manager = layout.state_manager;
if (layout.date_interval_slider === undefined){
layout.date_interval_slider = new DateRangeSlider({
container: '#date_interval',
outer_height: 100,
maxDelta: function(changed_date, other_date){
if (Math.abs(changed_date.getTime() - other_date.getTime()) > House.MAX_POWER_RANGE * 1000){
if (changed_date > other_date){
return new Date(changed_date.getTime() - House.MAX_POWER_RANGE * 1000);
} else {
return new Date(changed_date.getTime() + House.MAX_POWER_RANGE * 1000);
}
}
return false;
}
});
}
layout.date_interval_slider.onRangeUpdated = (min, max)=>{
if (layout.date_interval_update) clearTimeout(layout.date_interval_update);
// This will update the URL -> state_manager.state -> component states.
layout.date_interval_update = setTimeout(()=>{
var date_interval = [Math.round(min.getTime() / 1000), Math.round(max.getTime() / 1000)];
layout.state_manager.setParams({date_interval: date_interval}, layout);
}, 500);
};
var month_range = state_manager.month_range;
layout.date_interval_slider.drawData({
abs_min: house.toDate(month_range[0]),
abs_max: house.toDate(month_range[1]),
current_min: house.toDate(state_manager.state.date_interval[0]),
current_max: house.toDate(state_manager.state.date_interval[1])
});
}
refreshData(){
var layout = this,
houses = layout.state.houses,

View File

@@ -1,4 +1,5 @@
<rt-require dependency="./../energy/energy.component" as="Energy"/>
<rt-require dependency="./../irradiance/irradiance.component" as="Irradiance"/>
<rt-require dependency="./../power/power.component" as="Power"/>
<div id="layout">
<div rt-if="!this.house" id="about">
@@ -9,7 +10,7 @@
<ul>
<li>React</li>
<li>React Templates</li>
<li>React Router</li>
<li>ReactJs History</li>
<li>LokiJs - persisting API calls to indexedDb</li>
<li>Webpack - hot mode developing and app bundling</li>
<li>Babel - ES6 transpiler</li>
@@ -22,15 +23,23 @@
<div class="alert alert-warning" rt-if="this.state.loading_houses">Retrieving houses...</div>
<h4>Select household:</h4>
<select id="houses_select" rt-if="this.state.houses && this.state_manager" class="form-control" onChange="{this.setHouse.bind(this)}" value="{this.house_id}">
<option rt-repeat="house in this.state.houses" value="{house.data.id}" key="{house.scoped_id}">{house.data.name}</option>
</select>
<div rt-if="this.dataset !== 'irradiance'">
<h4>Select household:</h4>
<select id="houses_select" rt-if="this.state.houses && this.state_manager" class="form-control" onChange="{this.setHouse.bind(this)}" value="{this.house_id}">
<option rt-repeat="house in this.state.houses" value="{house.data.id}" key="{house.scoped_id}">{house.data.name}</option>
</select>
</div>
<button rt-if="this.house" onClick="{this.refreshData.bind(this)}" class="btn btn-xs btn-default">Refresh House Data</button>
<div rt-if="this.house">
<div>
<h4>Select dataset:</h4>
<div class="btn-group" role="group">
<button
data-param="dataset"
data-value="power"
rt-class="{active: this.state.dataset === 'power'}"
onClick="{this.setParam.bind(this)}"
type="button" class="btn btn-primary">15-minute Power Statistics</button>
<button
data-param="dataset"
data-value="energy"
@@ -39,10 +48,10 @@
type="button" class="btn btn-primary">Daily Energy Statistics</button>
<button
data-param="dataset"
data-value="power"
rt-class="{active: this.state.dataset === 'power'}"
data-value="irradiance"
rt-class="{active: this.state.dataset === 'irradiance'}"
onClick="{this.setParam.bind(this)}"
type="button" class="btn btn-primary">15-minute Power Statistics</button>
type="button" class="btn btn-primary">Daily Mean Irradiance</button>
</div>
<h4>View as:</h4>
@@ -72,26 +81,44 @@
class="btn-info btn btn-sm"
rt-class="{active: year == this.state.year}"
onClick="{this.setParam.bind(this)}">{year}</button>
</div><br/>
<div class="btn-group" rt-if="this.state.dataset === 'power' || this.state.dataset === 'irradiance'">
<button
rt-repeat="month in this.house.availableMonths(this.state.year)"
data-param="month"
data-value="{month}"
key="data-month-{month}"
class="btn-warning btn btn-sm"
rt-class="{active: month === this.state.month}"
onClick="{this.setParam.bind(this)}">{month}</button>
</div><br/>
<div id="date_interval"></div>
<div class="alert alert-warning" rt-if="this.state.loading_data">
Retrieving {this.state.loading_data} data...
</div>
</div><br/>
<Energy
rt-if="this.should_show_energy_data"
house="{this.state.house}"
loading_energy_data="{this.state.loading_energy_data}"
state_manager="{this.state_manager}"
view="{this.state.view}"
graph_attr="{this.state.graph_attr}"
year="{this.state.year}" />
<Irradiance
rt-if="this.state.dataset === 'irradiance'"
view="{this.state.view}"
date_interval="{this.state.date_interval}"
state_manager="{this.state_manager}" />
<Power
rt-if="this.should_show_power_data"
house="{this.state.house}"
loading_power_data="{this.state.loading_power_data}"
state_manager="{this.state_manager}"
view="{this.state.view}"
month="{this.state.month}"
year="{this.state.year}"
power_range="{this.state.power_range}" />
date_interval="{this.state.date_interval}" />
</div>
</div>

View File

@@ -1,8 +1,8 @@
import React from 'react';
import Templates from 'config/templates';
import c3 from 'c3';
import House from './../../../models/house';
import c3 from 'c3';
class GraphComponent extends React.Component {
@@ -21,7 +21,7 @@ class GraphComponent extends React.Component {
componentDidUpdate(prev_props, prev_state){
var power_graph = this;
if (prev_props.house != power_graph.props.house || prev_props.power_range != power_graph.props.power_range){
if (prev_props.house != power_graph.props.house || prev_props.date_interval != power_graph.props.date_interval){
power_graph.updateGraph();
}
}

View File

@@ -4,7 +4,6 @@ import _ from 'lodash';
import Templates from 'config/templates';
import House from './../../models/house';
import DateRangeSlider from './../../d3/sliders/date_range';
class PowerComponent extends React.Component {
@@ -25,75 +24,11 @@ class PowerComponent extends React.Component {
return this.props.state_manager;
}
get loading_power_data(){
return this.props.loading_power_data || this.state.loading_power_data;
}
componentDidMount(){
var power = this;
power.initDateRange();
}
componentDidUpdate(prev_props, prev_state){
var power = this,
state_manager = power.state_manager;
if (prev_props.power_range[0] != power.props.power_range[0] ||
prev_props.power_range[1] != power.props.power_range[1] ||
prev_props.house != power.props.house){
power.initDateRange();
state_manager.powerDataRendered();
}
}
syncFromStateManager(fnStateSet){
var power = this;
power.setState(power.state_manager.state, fnStateSet);
}
initDateRange(){
var power = this,
house = power.house;
if (power.date_range_slider === undefined){
power.date_range_slider = new DateRangeSlider({
container: '#power_date_setter',
outer_height: 100,
maxDelta: function(changed_date, other_date){
if (Math.abs(changed_date.getTime() - other_date.getTime()) > 3600 * 24 * 4 * 1000){
if (changed_date > other_date){
return new Date(changed_date.getTime() - 3600 * 24 * 4 * 1000);
} else {
return new Date(changed_date.getTime() + 3600 * 24 * 4 * 1000);
}
}
return false;
}
});
}
power.date_range_slider.onRangeUpdated = (min, max)=>{
if (power.date_range_update) clearTimeout(power.date_range_update);
power.date_range_update = setTimeout(()=>{
var power_range = [Math.round(min.getTime() / 1000), Math.round(max.getTime() / 1000)];
power.state_manager.setParams({power_range: power_range}, power);
}, 500);
};
power.date_range_slider.drawData({
abs_min: house.state.current_month_moment.toDate(),
abs_max: house.state.end_of_current_data_moment.toDate(),
current_min: house.toDate(house.state.power_range[0]),
current_max: house.toDate(house.state.power_range[1])
});
}
setParam(event){
var power = this,
param = event.target.dataset.param,
value = event.target.dataset.value,
update = {}, route_helper;
update[param] = value;
if (value == power.state_manager.state[param]) return false;
power.state_manager.setParams(update, power);
}
render() {
var powerRt = Templates.forComponent('power');
return powerRt.call(this);

View File

@@ -1,33 +1,18 @@
<rt-require dependency="./graph/graph.component" as="PowerGraph"/>
<rt-require dependency="./table/table.component" as="PowerTable"/>
<div id="power_view">
<div class="btn-group">
<button
rt-if="this.house"
rt-repeat="month in this.house.availableMonths()"
data-param="month"
data-value="{month}"
key="data-month-{month}"
class="btn-warning btn btn-sm"
rt-class="{active: month === this.house.state.month}"
onClick="{this.setParam.bind(this)}">{month}</button>
</div>
<div class="alert alert-warning" rt-if="this.loading_power_data">
Retrieving power data...
</div>
<div id="power_date_setter"></div>
<PowerGraph
rt-if="this.props.view === 'graph'"
state_manager="{this.props.state_manager}"
house="{this.props.house}"
month="{this.props.month}"
year="{this.props.year}"
power_range="{this.props.power_range}" ></PowerGraph>
date_interval="{this.props.date_interval}" ></PowerGraph>
<PowerTable
rt-if="this.props.view === 'table'"
state_manager="{this.props.state_manager}"
house="{this.props.house}"
month="{this.props.month}"
year="{this.props.year}"
power_range="{this.props.power_range}" ></PowerTable>
date_interval="{this.props.date_interval}" ></PowerTable>
</div>

View File

@@ -1,5 +1,7 @@
import query_string from 'query-string';
import moment from 'moment-timezone';
import EnergyDatum from './../models/energy_datum';
import ObjectUtil from './../../shared/utils/object';
import ArrayUtil from './../../shared/utils/array';
@@ -13,6 +15,9 @@ const ROUTES = [
}, {
path: /houses\/(\d+)\/(power)\/([^\/]+)\/(\d+)\/([^\/]+)\/?$/,
parameters: { 1: 'house_id', 2: 'dataset', 3: 'month', 4: 'year', 5: 'view' }
}, {
path: /(irradiance)\/([^\/]+)\/(\d+)\/([^\/]+)\/?$/,
parameters: { 1: 'dataset', 2: 'month', 3: 'year', 4: 'view' }
}
];
@@ -24,8 +29,7 @@ class StateManager {
state_manager.houses = houses;
state_manager.state = {
loading_energy_data: false,
loading_power_data: false,
loading_data: false,
graph_attr: 'consumption',
view: 'graph',
dataset: 'power',
@@ -33,55 +37,94 @@ class StateManager {
house: null,
month: null,
year: null,
power_range: null };
date_interval: null };
state_manager.history = createHistory();
state_manager.update_in_progress = false;
}
get month_i(){
return moment.monthsShort().indexOf(this.state.month);
}
get date_params(){
return ObjectUtil.filterKeys(this.state, ['year', 'month', 'power_range']);
return ObjectUtil.filterKeys(this.state, ['year', 'month', 'date_interval']);
}
get month_range(){
var state_manager = this,
house = state_manager.state.house,
start_time = house.parseMoment(`${state_manager.state.year}-${state_manager.month_i + 1}-01`, 'YYYY-M-DD'),
end_time = start_time.clone().endOf('month').unix();
start_time = start_time.unix();
if (start_time < house.data.data_from) start_time = house.data.data_from;
if (end_time > house.data.data_until) end_time = house.data.data_until;
return [start_time, end_time];
}
get year_range(){
var state_manager = this,
house = state_manager.state.house,
start_time = house.parseMoment(`${state_manager.state.year}-01-01`, 'YYYY-MM-DD'),
end_time = start_time.clone().endOf('year').unix();
start_time = start_time.unix();
if (start_time < house.data.data_from) start_time = house.data.data_from;
if (end_time > house.data.data_until) end_time = house.data.data_until;
return [start_time, end_time];
}
matchesEnergyState(){
var state_manager = this,
house = state_manager.state.house,
energy_range = state_manager.state.graph_attr === 'irradiance' ? state_manager.state.date_interval : state_manager.year_range;
if (!house.state.energy_range) return false;
return energy_range[0] === house.state.energy_range[0] && energy_range[1] === house.state.energy_range[1];
}
matchesPowerState(){
var state_manager = this,
house = state_manager.state.house,
month_range = state_manager.month_range;
if (!house.state.power_range) return false;
return month_range[0] === house.state.power_range[0] && month_range[1] === house.state.power_range[1];
}
// This will update the house state acccording to passed update parameters.
updateHouseFromState(component, fnResolve){
updateHouseFromState(component){
var state_manager = this,
house = state_manager.state.house,
promise;
if (!house) {
promise = Promise.resolve();
} else if (state_manager.state.dataset === 'energy' && (!house.energy_data || !house.matchesEnergyState(state_manager.state))){
house.setMonthState(state_manager.state);
} else if (state_manager.state.dataset === 'energy' && !state_manager.matchesEnergyState()){
promise = state_manager.setHouseEnergyFromState(component);
} else if (state_manager.state.dataset === 'power' && !house.power_data || !house.matchesPowerState(state_manager.state)){
house.setMonthState(state_manager.state);
} else if (state_manager.state.dataset === 'power' && !state_manager.matchesPowerState()){
promise = state_manager.setHousePowerFromState(component);
} else if (state_manager.state.dataset === 'irradiance'){
promise = state_manager.setIrradianceData(component);
} else {
promise = new Promise((fnResolve, fnReject)=>{
component.syncFromStateManager(fnResolve);
});
promise = Promise.resolve();
}
return promise.then(()=>{ state_manager.update_in_progress = false; })
}
setHouseEnergyFromState(component){
var state_manager = this;
state_manager.power_data_updated = true;
return new Promise((fnResolve, fnReject)=>{
component.setState({
loading_energy_data: true
}, ()=>{
state_manager.state.house.setEnergyData()
.then(()=>{
component.syncFromStateManager(fnResolve);
});
return promise.then(()=>{
state_manager.update_in_progress = false;
return new Promise((fnResolve, fnReject)=>{
component.syncFromStateManager(fnResolve);
});
});
}
powerDataRendered(){
setHouseEnergyFromState(component){
var state_manager = this;
state_manager.power_data_updated = false;
return new Promise((fnResolve, fnReject)=>{
component.setState({
loading_data: 'power'
}, ()=>{
state_manager.state.house.setEnergyData(state_manager.year_range)
.then(fnResolve);
});
});
}
setHousePowerFromState(component){
@@ -89,11 +132,49 @@ class StateManager {
house = state_manager.state.house;
return new Promise((fnResolve, fnReject)=>{
component.setState({
loading_power_data: true
loading_data: 'energy'
}, ()=>{
house.setPowerData()
.then(()=>{
component.syncFromStateManager(fnResolve);
house.setPowerData(state_manager.state.date_interval)
.then(fnResolve);
});
});
}
setIrradianceData(component){
var state_manager = this,
houses = state_manager.houses,
date_interval = state_manager.state.date_interval;
return new Promise((fnResolve, fnReject)=>{
component.setState({
loading_data: 'irradiance'
}, ()=>{
EnergyDatum.ensureEnergyDataForHouses(houses, date_interval)
.then((res)=>{
if (res instanceof Promise){
throw new Error('promise returned promise')
}
var promises = [],
data = {};
houses.forEach((house)=>{
var promise = house.setEnergyData(date_interval)
.then(()=>{
house.energy_data.forEach((energy_datum)=>{
var date_data = data[energy_datum.day_to_s];
if (!date_data){
date_data = [];
data[energy_datum.day_to_s] = date_data;
}
date_data.push(energy_datum);
});
house.closeDb();
});
promises.push(promise);
});
Promise.all(promises)
.then(()=>{
state_manager.irradiance_data = data;
fnResolve();
});
});
});
});
@@ -105,25 +186,29 @@ class StateManager {
setParams(params){
var state_manager = this,
url;
url, house, params;
if (state_manager.update_in_progress) return false;
state_manager.update_in_progress = true;
params = Object.assign({}, state_manager.state, params);
if (!params.house_id){
url = '/';
} else {
var house = state_manager.houses.find((h)=>{ return h.data.id == params.house_id; })
house.verifyMonthState(params);
if (params.dataset === 'energy'){
url = `/houses/${params.house_id}/energy/${params.year}/${params.graph_attr}/${params.view}`;
} else if (params.dataset === 'power'){
house.verifyPowerRange(params);
url = `/houses/${params.house_id}/power/${params.month}/${params.year}/${params.view}?${query_string.stringify({dates: params.power_range})}`;
} else {
url = `/houses/${house.house_id}`;
}
params = Object.assign({}, state_manager.state, params);
if (params.house_id){
house = state_manager.houses.find((h)=>{ return h.data.id == params.house_id; });
} else {
house = state_manager.state.house || state_manager.houses[0];
params.house_id = house.data.id;
}
house.verifyMonthState(params);
if (params.dataset === 'irradiance'){
params.date_interval = house.verifyPowerRange(params.date_interval || [], params);
url = `/irradiance/${params.month}/${params.year}/${params.view}?${query_string.stringify({dates: params.date_interval})}`;
} else if (params.dataset === 'energy'){
url = `/houses/${params.house_id}/energy/${params.year}/${params.graph_attr}/${params.view}`;
} else {
params.date_interval = house.verifyPowerRange(params.date_interval || [], params);
url = `/houses/${params.house_id}/power/${params.month}/${params.year}/${params.view}?${query_string.stringify({dates: params.date_interval})}`;
}
state_manager.history.push(url);
}
@@ -132,16 +217,28 @@ class StateManager {
*/
updateStateFromUrl(location, component){
var state_manager = this;
var params = state_manager.parseUrl(location.pathname),
var state_manager = this,
params = state_manager.parseUrl(location.pathname),
house = null;
if (params.dataset === 'power' && location.query.dates) {
params.power_range = [+location.query.dates[0], +location.query.dates[1]];
}
if (params.house_id || params.house_id != state_manager.state.house_id){
if (params.house_id){
house = state_manager.houses.find((h)=>{ return h.data.id == params.house_id; });
} else if (params.dataset === 'irradiance'){
// Irradiance needs a house to verify params and
house = state_manager.state.house || state_manager.houses[0];
}
state_manager.state.house = house;
if (house){
// params should already be verified if set through StateManager#setParams, but
// verify here again before setting state in case URL manually loaded.
house.verifyMonthState(params);
if (params.dataset === 'power' || params.dataset === 'irradiance') {
var date_interval = location.query.dates || [];
params.date_interval = house.verifyPowerRange([+date_interval[0], +date_interval[1]], params);
}
state_manager.state.house = house;
state_manager.state.house_id = house.data.id;
}
Object.assign(state_manager.state, params);
if (state_manager.state.house_id) {
state_manager.updateHouseFromState(component);