mirror of
https://github.com/aclindsa/moneygo.git
synced 2024-12-26 15:42:27 -05:00
reports: Allow drilling down
This commit is contained in:
parent
4d642d1772
commit
b443963375
@ -55,7 +55,6 @@ func luaGetAccounts(L *lua.LState) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registers my account type to given L.
|
|
||||||
func luaRegisterAccounts(L *lua.LState) {
|
func luaRegisterAccounts(L *lua.LState) {
|
||||||
mt := L.NewTypeMetatable(luaAccountTypeName)
|
mt := L.NewTypeMetatable(luaAccountTypeName)
|
||||||
L.SetGlobal("account", mt)
|
L.SetGlobal("account", mt)
|
||||||
|
@ -12,7 +12,6 @@ type Balance struct {
|
|||||||
|
|
||||||
const luaBalanceTypeName = "balance"
|
const luaBalanceTypeName = "balance"
|
||||||
|
|
||||||
// Registers my balance type to given L.
|
|
||||||
func luaRegisterBalances(L *lua.LState) {
|
func luaRegisterBalances(L *lua.LState) {
|
||||||
mt := L.NewTypeMetatable(luaBalanceTypeName)
|
mt := L.NewTypeMetatable(luaBalanceTypeName)
|
||||||
L.SetGlobal("balance", mt)
|
L.SetGlobal("balance", mt)
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
const luaDateTypeName = "date"
|
const luaDateTypeName = "date"
|
||||||
const timeFormat = "2006-01-02"
|
const timeFormat = "2006-01-02"
|
||||||
|
|
||||||
// Registers my date type to given L.
|
|
||||||
func luaRegisterDates(L *lua.LState) {
|
func luaRegisterDates(L *lua.LState) {
|
||||||
mt := L.NewTypeMetatable(luaDateTypeName)
|
mt := L.NewTypeMetatable(luaDateTypeName)
|
||||||
L.SetGlobal("date", mt)
|
L.SetGlobal("date", mt)
|
||||||
|
@ -21,6 +21,17 @@ function ajaxError(error) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clientError(error) {
|
||||||
|
var e = new Error();
|
||||||
|
e.ErrorId = 999;
|
||||||
|
e.ErrorString = "Client Error: " + error;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: ErrorConstants.ERROR_CLIENT,
|
||||||
|
error: e
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function clearError() {
|
function clearError() {
|
||||||
return {
|
return {
|
||||||
type: ErrorConstants.CLEAR_ERROR,
|
type: ErrorConstants.CLEAR_ERROR,
|
||||||
@ -30,5 +41,6 @@ function clearError() {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
serverError: serverError,
|
serverError: serverError,
|
||||||
ajaxError: ajaxError,
|
ajaxError: ajaxError,
|
||||||
|
clientError: clientError,
|
||||||
clearError: clearError
|
clearError: clearError
|
||||||
};
|
};
|
||||||
|
@ -6,9 +6,10 @@ var models = require('../models.js');
|
|||||||
var Report = models.Report;
|
var Report = models.Report;
|
||||||
var Error = models.Error;
|
var Error = models.Error;
|
||||||
|
|
||||||
function fetchReport() {
|
function fetchReport(reportName) {
|
||||||
return {
|
return {
|
||||||
type: ReportConstants.FETCH_REPORT
|
type: ReportConstants.FETCH_REPORT,
|
||||||
|
reportName: reportName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,9 +20,25 @@ function reportFetched(report) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectReport(report, seriesTraversal) {
|
||||||
|
return {
|
||||||
|
type: ReportConstants.SELECT_REPORT,
|
||||||
|
report: report,
|
||||||
|
seriesTraversal: seriesTraversal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportSelected(flattenedReport, seriesTraversal) {
|
||||||
|
return {
|
||||||
|
type: ReportConstants.REPORT_SELECTED,
|
||||||
|
report: flattenedReport,
|
||||||
|
seriesTraversal: seriesTraversal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function fetch(report) {
|
function fetch(report) {
|
||||||
return function (dispatch) {
|
return function (dispatch) {
|
||||||
dispatch(fetchReport());
|
dispatch(fetchReport(report));
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
@ -45,6 +62,48 @@ function fetch(report) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function select(report, seriesTraversal) {
|
||||||
|
return function (dispatch) {
|
||||||
|
if (!seriesTraversal)
|
||||||
|
seriesTraversal = [];
|
||||||
|
dispatch(selectReport(report, seriesTraversal));
|
||||||
|
|
||||||
|
// Descend the tree to the right series to flatten
|
||||||
|
var series = report;
|
||||||
|
for (var i=0; i < seriesTraversal.length; i++) {
|
||||||
|
if (!series.Series.hasOwnProperty(seriesTraversal[i])) {
|
||||||
|
dispatch(ErrorActions.clientError("Invalid series"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
series = series.Series[seriesTraversal[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually flatten the data
|
||||||
|
var flattenedSeries = series.mapReduceChildren(null,
|
||||||
|
function(accumulator, currentValue, currentIndex, array) {
|
||||||
|
return accumulator + currentValue;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add back in any values from the current level
|
||||||
|
if (series.hasOwnProperty('Values'))
|
||||||
|
flattenedSeries[report.topLevelAccountName] = series.Values;
|
||||||
|
|
||||||
|
var flattenedReport = new Report();
|
||||||
|
|
||||||
|
flattenedReport.ReportId = report.ReportId;
|
||||||
|
flattenedReport.Title = report.Title;
|
||||||
|
flattenedReport.Subtitle = report.Subtitle;
|
||||||
|
flattenedReport.XAxisLabel = report.XAxisLabel;
|
||||||
|
flattenedReport.YAxisLabel = report.YAxisLabel;
|
||||||
|
flattenedReport.Labels = report.Labels.slice();
|
||||||
|
flattenedReport.FlattenedSeries = flattenedSeries;
|
||||||
|
|
||||||
|
dispatch(reportSelected(flattenedReport, seriesTraversal));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
fetch: fetch
|
fetch: fetch,
|
||||||
|
select: select
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap');
|
||||||
|
|
||||||
|
var Button = ReactBootstrap.Button;
|
||||||
|
var Panel = ReactBootstrap.Panel;
|
||||||
|
|
||||||
var StackedBarChart = require('../components/StackedBarChart');
|
var StackedBarChart = require('../components/StackedBarChart');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
@ -10,10 +15,69 @@ module.exports = React.createClass({
|
|||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
this.props.onFetchReport("monthly_expenses");
|
this.props.onFetchReport("monthly_expenses");
|
||||||
},
|
},
|
||||||
|
componentWillReceiveProps: function(nextProps) {
|
||||||
|
if (nextProps.reports['monthly_expenses'] && !nextProps.selectedReport.report) {
|
||||||
|
this.props.onSelectReport(nextProps.reports['monthly_expenses'], []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSelectSeries: function(series) {
|
||||||
|
if (series == this.props.selectedReport.report.topLevelAccountName)
|
||||||
|
return;
|
||||||
|
var seriesTraversal = this.props.selectedReport.seriesTraversal.slice();
|
||||||
|
seriesTraversal.push(series);
|
||||||
|
this.props.onSelectReport(this.props.reports[this.props.selectedReport.report.ReportId], seriesTraversal);
|
||||||
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
report = [];
|
var report = [];
|
||||||
if (this.props.reports['monthly_expenses'])
|
if (this.props.selectedReport.report) {
|
||||||
report = (<StackedBarChart data={this.props.reports['monthly_expenses']} />);
|
var titleTracks = [];
|
||||||
|
var seriesTraversal = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < this.props.selectedReport.seriesTraversal.length; i++) {
|
||||||
|
var name = this.props.selectedReport.report.Title;
|
||||||
|
if (i > 0)
|
||||||
|
name = this.props.selectedReport.seriesTraversal[i-1];
|
||||||
|
|
||||||
|
// Make a closure for going up the food chain
|
||||||
|
var self = this;
|
||||||
|
var navOnClick = function() {
|
||||||
|
var onSelectReport = self.props.onSelectReport;
|
||||||
|
var report = self.props.reports[self.props.selectedReport.report.ReportId];
|
||||||
|
var mySeriesTraversal = seriesTraversal.slice();
|
||||||
|
return function() {
|
||||||
|
onSelectReport(report, mySeriesTraversal);
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
titleTracks.push((
|
||||||
|
<Button bsStyle="link"
|
||||||
|
onClick={navOnClick}>
|
||||||
|
{name}
|
||||||
|
</Button>
|
||||||
|
));
|
||||||
|
titleTracks.push((<span>/</span>));
|
||||||
|
seriesTraversal.push(this.props.selectedReport.seriesTraversal[i]);
|
||||||
|
}
|
||||||
|
if (titleTracks.length == 0)
|
||||||
|
titleTracks.push((
|
||||||
|
<Button bsStyle="link">
|
||||||
|
{this.props.selectedReport.report.Title}
|
||||||
|
</Button>
|
||||||
|
));
|
||||||
|
else
|
||||||
|
titleTracks.push((
|
||||||
|
<Button bsStyle="link">
|
||||||
|
{this.props.selectedReport.seriesTraversal[this.props.selectedReport.seriesTraversal.length-1]}
|
||||||
|
</Button>
|
||||||
|
));
|
||||||
|
|
||||||
|
report = (<Panel header={titleTracks}>
|
||||||
|
<StackedBarChart
|
||||||
|
report={this.props.selectedReport.report}
|
||||||
|
onSelectSeries={this.onSelectSeries}
|
||||||
|
seriesTraversal={this.props.selectedReport.seriesTraversal} />
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{report}
|
{report}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
var d3 = require('d3');
|
var d3 = require('d3');
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var Panel = require('react-bootstrap').Panel;
|
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: "StackedBarChart",
|
displayName: "StackedBarChart",
|
||||||
calcMinMax: function(data) {
|
calcMinMax: function(series) {
|
||||||
var children = [];
|
var children = [];
|
||||||
for (var child in data) {
|
for (var child in series) {
|
||||||
if (data.hasOwnProperty(child))
|
if (series.hasOwnProperty(child))
|
||||||
children.push(data[child]);
|
children.push(series[child]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var positiveValues = [0];
|
var positiveValues = [0];
|
||||||
@ -40,12 +38,6 @@ module.exports = React.createClass({
|
|||||||
return Math.ceil(rangePerTick/roundTo)*roundTo;
|
return Math.ceil(rangePerTick/roundTo)*roundTo;
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var data = this.props.data.mapReduceChildren(null,
|
|
||||||
function(accumulator, currentValue, currentIndex, array) {
|
|
||||||
return accumulator + currentValue;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
height = 400;
|
height = 400;
|
||||||
width = 600;
|
width = 600;
|
||||||
legendWidth = 100;
|
legendWidth = 100;
|
||||||
@ -54,7 +46,7 @@ module.exports = React.createClass({
|
|||||||
height -= yMargin*2;
|
height -= yMargin*2;
|
||||||
width -= xMargin*2;
|
width -= xMargin*2;
|
||||||
|
|
||||||
var minMax = this.calcMinMax(data);
|
var minMax = this.calcMinMax(this.props.report.FlattenedSeries);
|
||||||
var y = d3.scaleLinear()
|
var y = d3.scaleLinear()
|
||||||
.range([0, height])
|
.range([0, height])
|
||||||
.domain(minMax);
|
.domain(minMax);
|
||||||
@ -63,7 +55,7 @@ module.exports = React.createClass({
|
|||||||
|
|
||||||
var x = d3.scaleLinear()
|
var x = d3.scaleLinear()
|
||||||
.range([0, width])
|
.range([0, width])
|
||||||
.domain([0, this.props.data.Labels.length + 0.5]);
|
.domain([0, this.props.report.Labels.length + 0.5]);
|
||||||
|
|
||||||
var bars = [];
|
var bars = [];
|
||||||
var labels = [];
|
var labels = [];
|
||||||
@ -76,13 +68,13 @@ module.exports = React.createClass({
|
|||||||
// negativeSum arrays
|
// negativeSum arrays
|
||||||
var positiveSum = [];
|
var positiveSum = [];
|
||||||
var negativeSum = [];
|
var negativeSum = [];
|
||||||
for (var i=0; i < this.props.data.Labels.length; i++) {
|
for (var i=0; i < this.props.report.Labels.length; i++) {
|
||||||
positiveSum.push(0);
|
positiveSum.push(0);
|
||||||
negativeSum.push(0);
|
negativeSum.push(0);
|
||||||
var labelX = x(i) + barStart + barWidth/2;
|
var labelX = x(i) + barStart + barWidth/2;
|
||||||
var labelY = height + 15;
|
var labelY = height + 15;
|
||||||
labels.push((
|
labels.push((
|
||||||
<text x={labelX} y={labelY} transform={"rotate(45 "+labelX+" "+labelY+")"}>{this.props.data.Labels[i]}</text>
|
<text x={labelX} y={labelY} transform={"rotate(45 "+labelX+" "+labelY+")"}>{this.props.report.Labels[i]}</text>
|
||||||
));
|
));
|
||||||
labels.push((
|
labels.push((
|
||||||
<line className="axis-tick" x1={labelX} y1={height-3} x2={labelX} y2={height+3} />
|
<line className="axis-tick" x1={labelX} y1={height-3} x2={labelX} y2={height+3} />
|
||||||
@ -103,14 +95,24 @@ module.exports = React.createClass({
|
|||||||
for (var i=0-xAxisMarksEvery; i > minMax[0]; i -= xAxisMarksEvery)
|
for (var i=0-xAxisMarksEvery; i > minMax[0]; i -= xAxisMarksEvery)
|
||||||
makeXLabel(i);
|
makeXLabel(i);
|
||||||
|
|
||||||
//TODO handle Values from current series?
|
|
||||||
var legendMap = {};
|
var legendMap = {};
|
||||||
for (var child in data) {
|
for (var child in this.props.report.FlattenedSeries) {
|
||||||
childId++;
|
if (this.props.report.FlattenedSeries.hasOwnProperty(child)) {
|
||||||
var rectClasses = "chart-element chart-color" + (childId % 12);
|
childId++;
|
||||||
if (data.hasOwnProperty(child)) {
|
var childData = this.props.report.FlattenedSeries[child];
|
||||||
for (var i=0; i < data[child].length; i++) {
|
var rectClasses = "chart-element chart-color" + (childId % 12);
|
||||||
var value = data[child][i];
|
var self = this;
|
||||||
|
var rectOnClick = function() {
|
||||||
|
var childName = child;
|
||||||
|
var onSelectSeries = self.props.onSelectSeries;
|
||||||
|
return function() {
|
||||||
|
onSelectSeries(childName);
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
|
var seriesBars = [];
|
||||||
|
for (var i=0; i < childData.length; i++) {
|
||||||
|
var value = childData[i];
|
||||||
if (value == 0)
|
if (value == 0)
|
||||||
continue;
|
continue;
|
||||||
legendMap[child] = childId;
|
legendMap[child] = childId;
|
||||||
@ -124,10 +126,15 @@ module.exports = React.createClass({
|
|||||||
negativeSum[i] += rectHeight;
|
negativeSum[i] += rectHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
bars.push((
|
seriesBars.push((
|
||||||
<rect className={rectClasses} x={x(i) + barStart} y={rectY} width={barWidth} height={rectHeight} rx={1} ry={1}/>
|
<rect onClick={rectOnClick} className={rectClasses} x={x(i) + barStart} y={rectY} width={barWidth} height={rectHeight} rx={1} ry={1}/>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
bars.push((
|
||||||
|
<g className="chart-series">
|
||||||
|
{seriesBars}
|
||||||
|
</g>
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,19 +151,17 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Panel header={this.props.data.Title}>
|
<svg height={height + 2*yMargin} width={width + 2*xMargin + legendWidth}>
|
||||||
<svg height={height + 2*yMargin} width={width + 2*xMargin + legendWidth}>
|
<g className="stacked-bar-chart" transform={"translate("+xMargin+" "+yMargin+")"}>
|
||||||
<g className="stacked-bar-chart" transform={"translate("+xMargin+" "+yMargin+")"}>
|
{bars}
|
||||||
{bars}
|
<line className="axis x-axis" x1={0} y1={height} x2={width} y2={height} />
|
||||||
<line className="axis x-axis" x1={0} y1={height} x2={width} y2={height} />
|
<line className="axis y-axis" x1={0} y1={0} x2={0} y2={height} />
|
||||||
<line className="axis y-axis" x1={0} y1={0} x2={0} y2={height} />
|
{labels}
|
||||||
{labels}
|
</g>
|
||||||
</g>
|
<g className="chart-legend" transform={"translate("+(width + 2*xMargin)+" "+yMargin+")"}>
|
||||||
<g className="chart-legend" transform={"translate("+(width + 2*xMargin)+" "+yMargin+")"}>
|
{legend}
|
||||||
{legend}
|
</g>
|
||||||
</g>
|
</svg>
|
||||||
</svg>
|
|
||||||
</Panel>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,5 +2,7 @@ var keyMirror = require('keymirror');
|
|||||||
|
|
||||||
module.exports = keyMirror({
|
module.exports = keyMirror({
|
||||||
FETCH_REPORT: null,
|
FETCH_REPORT: null,
|
||||||
REPORT_FETCHED: null
|
REPORT_FETCHED: null,
|
||||||
|
SELECT_REPORT: null,
|
||||||
|
REPORT_SELECTED: null
|
||||||
});
|
});
|
||||||
|
@ -5,13 +5,15 @@ var ReportsTab = require('../components/ReportsTab');
|
|||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
reports: state.reports
|
reports: state.reports,
|
||||||
|
selectedReport: state.selectedReport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
function mapDispatchToProps(dispatch) {
|
||||||
return {
|
return {
|
||||||
onFetchReport: function(reportname) {dispatch(ReportActions.fetch(reportname))}
|
onFetchReport: function(reportname) {dispatch(ReportActions.fetch(reportname))},
|
||||||
|
onSelectReport: function(report, seriesTraversal) {dispatch(ReportActions.select(report, seriesTraversal))}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
js/models.js
35
js/models.js
@ -399,16 +399,16 @@ Error.prototype.isError = function() {
|
|||||||
|
|
||||||
function Series() {
|
function Series() {
|
||||||
this.Values = [];
|
this.Values = [];
|
||||||
this.Children = {};
|
this.Series = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Series.prototype.toJSONobj = function() {
|
Series.prototype.toJSONobj = function() {
|
||||||
var json_obj = {};
|
var json_obj = {};
|
||||||
json_obj.Values = this.Values;
|
json_obj.Values = this.Values;
|
||||||
json_obj.Children = {};
|
json_obj.Series = {};
|
||||||
for (var child in this.Children) {
|
for (var child in this.Series) {
|
||||||
if (this.Children.hasOwnProperty(child))
|
if (this.Series.hasOwnProperty(child))
|
||||||
json_obj.Children[child] = this.Children[child].toJSONobj();
|
json_obj.Series[child] = this.Series[child].toJSONobj();
|
||||||
}
|
}
|
||||||
return json_obj;
|
return json_obj;
|
||||||
}
|
}
|
||||||
@ -416,20 +416,20 @@ Series.prototype.toJSONobj = function() {
|
|||||||
Series.prototype.fromJSONobj = function(json_obj) {
|
Series.prototype.fromJSONobj = function(json_obj) {
|
||||||
if (json_obj.hasOwnProperty("Values"))
|
if (json_obj.hasOwnProperty("Values"))
|
||||||
this.Values = json_obj.Values;
|
this.Values = json_obj.Values;
|
||||||
if (json_obj.hasOwnProperty("Children")) {
|
if (json_obj.hasOwnProperty("Series")) {
|
||||||
for (var child in json_obj.Children) {
|
for (var child in json_obj.Series) {
|
||||||
if (json_obj.Children.hasOwnProperty(child))
|
if (json_obj.Series.hasOwnProperty(child))
|
||||||
this.Children[child] = new Series();
|
this.Series[child] = new Series();
|
||||||
this.Children[child].fromJSONobj(json_obj.Children[child]);
|
this.Series[child].fromJSONobj(json_obj.Series[child]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Series.prototype.mapReduceChildren = function(mapFn, reduceFn) {
|
Series.prototype.mapReduceChildren = function(mapFn, reduceFn) {
|
||||||
var children = {}
|
var children = {}
|
||||||
for (var child in this.Children) {
|
for (var child in this.Series) {
|
||||||
if (this.Children.hasOwnProperty(child))
|
if (this.Series.hasOwnProperty(child))
|
||||||
children[child] = this.Children[child].mapReduce(mapFn, reduceFn);
|
children[child] = this.Series[child].mapReduce(mapFn, reduceFn);
|
||||||
}
|
}
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
@ -441,9 +441,9 @@ Series.prototype.mapReduce = function(mapFn, reduceFn) {
|
|||||||
else
|
else
|
||||||
childValues.push(this.Values.slice());
|
childValues.push(this.Values.slice());
|
||||||
|
|
||||||
for (var child in this.Children) {
|
for (var child in this.Series) {
|
||||||
if (this.Children.hasOwnProperty(child))
|
if (this.Series.hasOwnProperty(child))
|
||||||
childValues.push(this.Children[child].mapReduce(mapFn, reduceFn));
|
childValues.push(this.Series[child].mapReduce(mapFn, reduceFn));
|
||||||
}
|
}
|
||||||
|
|
||||||
var reducedValues = [];
|
var reducedValues = [];
|
||||||
@ -466,8 +466,11 @@ function Report() {
|
|||||||
this.YAxisLabel = "";
|
this.YAxisLabel = "";
|
||||||
this.Labels = [];
|
this.Labels = [];
|
||||||
this.Series = {};
|
this.Series = {};
|
||||||
|
this.FlattenedSeries = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Report.prototype.topLevelAccountName = "(top level)";
|
||||||
|
|
||||||
Report.prototype.toJSON = function() {
|
Report.prototype.toJSON = function() {
|
||||||
var json_obj = {};
|
var json_obj = {};
|
||||||
json_obj.ReportId = this.ReportId;
|
json_obj.ReportId = this.ReportId;
|
||||||
|
@ -8,6 +8,7 @@ var SecurityTemplateReducer = require('./SecurityTemplateReducer');
|
|||||||
var SelectedAccountReducer = require('./SelectedAccountReducer');
|
var SelectedAccountReducer = require('./SelectedAccountReducer');
|
||||||
var SelectedSecurityReducer = require('./SelectedSecurityReducer');
|
var SelectedSecurityReducer = require('./SelectedSecurityReducer');
|
||||||
var ReportReducer = require('./ReportReducer');
|
var ReportReducer = require('./ReportReducer');
|
||||||
|
var SelectedReportReducer = require('./SelectedReportReducer');
|
||||||
var ErrorReducer = require('./ErrorReducer');
|
var ErrorReducer = require('./ErrorReducer');
|
||||||
|
|
||||||
module.exports = Redux.combineReducers({
|
module.exports = Redux.combineReducers({
|
||||||
@ -19,5 +20,6 @@ module.exports = Redux.combineReducers({
|
|||||||
selectedAccount: SelectedAccountReducer,
|
selectedAccount: SelectedAccountReducer,
|
||||||
selectedSecurity: SelectedSecurityReducer,
|
selectedSecurity: SelectedSecurityReducer,
|
||||||
reports: ReportReducer,
|
reports: ReportReducer,
|
||||||
|
selectedReport: SelectedReportReducer,
|
||||||
error: ErrorReducer
|
error: ErrorReducer
|
||||||
});
|
});
|
||||||
|
23
js/reducers/SelectedReportReducer.js
Normal file
23
js/reducers/SelectedReportReducer.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
var assign = require('object-assign');
|
||||||
|
|
||||||
|
var ReportConstants = require('../constants/ReportConstants');
|
||||||
|
var UserConstants = require('../constants/UserConstants');
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
report: null,
|
||||||
|
seriesTraversal: []
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function(state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case ReportConstants.REPORT_SELECTED:
|
||||||
|
return {
|
||||||
|
report: action.report,
|
||||||
|
seriesTraversal: action.seriesTraversal
|
||||||
|
};
|
||||||
|
case UserConstants.USER_LOGGEDOUT:
|
||||||
|
return initialState;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
@ -25,8 +25,8 @@ const (
|
|||||||
const luaTimeoutSeconds time.Duration = 5 // maximum time a lua request can run for
|
const luaTimeoutSeconds time.Duration = 5 // maximum time a lua request can run for
|
||||||
|
|
||||||
type Series struct {
|
type Series struct {
|
||||||
Values []float64
|
Values []float64
|
||||||
Children map[string]*Series
|
Series map[string]*Series
|
||||||
}
|
}
|
||||||
|
|
||||||
type Report struct {
|
type Report struct {
|
||||||
|
@ -97,8 +97,8 @@ func luaReportSeries(L *lua.LState) int {
|
|||||||
ud.Value = s
|
ud.Value = s
|
||||||
} else {
|
} else {
|
||||||
report.Series[name] = &Series{
|
report.Series[name] = &Series{
|
||||||
Children: make(map[string]*Series),
|
Series: make(map[string]*Series),
|
||||||
Values: make([]float64, cap(report.Labels)),
|
Values: make([]float64, cap(report.Labels)),
|
||||||
}
|
}
|
||||||
ud.Value = report.Series[name]
|
ud.Value = report.Series[name]
|
||||||
}
|
}
|
||||||
@ -157,8 +157,8 @@ func luaSeries__index(L *lua.LState) int {
|
|||||||
switch field {
|
switch field {
|
||||||
case "Value", "value":
|
case "Value", "value":
|
||||||
L.Push(L.NewFunction(luaSeriesValue))
|
L.Push(L.NewFunction(luaSeriesValue))
|
||||||
case "Series", "series", "Child", "child":
|
case "Series", "series":
|
||||||
L.Push(L.NewFunction(luaSeriesChildren))
|
L.Push(L.NewFunction(luaSeriesSeries))
|
||||||
default:
|
default:
|
||||||
L.ArgError(2, "unexpected series attribute: "+field)
|
L.ArgError(2, "unexpected series attribute: "+field)
|
||||||
}
|
}
|
||||||
@ -179,20 +179,20 @@ func luaSeriesValue(L *lua.LState) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func luaSeriesChildren(L *lua.LState) int {
|
func luaSeriesSeries(L *lua.LState) int {
|
||||||
parent := luaCheckSeries(L, 1)
|
parent := luaCheckSeries(L, 1)
|
||||||
name := L.CheckString(2)
|
name := L.CheckString(2)
|
||||||
ud := L.NewUserData()
|
ud := L.NewUserData()
|
||||||
|
|
||||||
s, ok := parent.Children[name]
|
s, ok := parent.Series[name]
|
||||||
if ok {
|
if ok {
|
||||||
ud.Value = s
|
ud.Value = s
|
||||||
} else {
|
} else {
|
||||||
parent.Children[name] = &Series{
|
parent.Series[name] = &Series{
|
||||||
Children: make(map[string]*Series),
|
Series: make(map[string]*Series),
|
||||||
Values: make([]float64, cap(parent.Values)),
|
Values: make([]float64, cap(parent.Values)),
|
||||||
}
|
}
|
||||||
ud.Value = parent.Children[name]
|
ud.Value = parent.Series[name]
|
||||||
}
|
}
|
||||||
L.SetMetatable(ud, L.GetTypeMetatable(luaSeriesTypeName))
|
L.SetMetatable(ud, L.GetTypeMetatable(luaSeriesTypeName))
|
||||||
L.Push(ud)
|
L.Push(ud)
|
||||||
|
@ -53,7 +53,6 @@ func luaGetSecurities(L *lua.LState) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registers my security type to given L.
|
|
||||||
func luaRegisterSecurities(L *lua.LState) {
|
func luaRegisterSecurities(L *lua.LState) {
|
||||||
mt := L.NewTypeMetatable(luaSecurityTypeName)
|
mt := L.NewTypeMetatable(luaSecurityTypeName)
|
||||||
L.SetGlobal("security", mt)
|
L.SetGlobal("security", mt)
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
.chart-color8 {
|
.chart-color8 {
|
||||||
fill: #ff7f00;
|
fill: #ff7f00;
|
||||||
fill: #df5f00;
|
stroke: #df5f00;
|
||||||
}
|
}
|
||||||
.chart-color9 {
|
.chart-color9 {
|
||||||
fill: #cab2d6;
|
fill: #cab2d6;
|
||||||
@ -50,7 +50,7 @@
|
|||||||
.chart-element {
|
.chart-element {
|
||||||
stroke-width: 0;
|
stroke-width: 0;
|
||||||
}
|
}
|
||||||
.chart-element:hover {
|
g.chart-series:hover .chart-element {
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
.chart-legend rect {
|
.chart-legend rect {
|
||||||
|
Loading…
Reference in New Issue
Block a user