mirror of
				https://github.com/aclindsa/moneygo.git
				synced 2025-11-03 18:13:27 -05:00 
			
		
		
		
	reports: Allow drilling down
This commit is contained in:
		@@ -55,7 +55,6 @@ func luaGetAccounts(L *lua.LState) int {
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registers my account type to given L.
 | 
			
		||||
func luaRegisterAccounts(L *lua.LState) {
 | 
			
		||||
	mt := L.NewTypeMetatable(luaAccountTypeName)
 | 
			
		||||
	L.SetGlobal("account", mt)
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ type Balance struct {
 | 
			
		||||
 | 
			
		||||
const luaBalanceTypeName = "balance"
 | 
			
		||||
 | 
			
		||||
// Registers my balance type to given L.
 | 
			
		||||
func luaRegisterBalances(L *lua.LState) {
 | 
			
		||||
	mt := L.NewTypeMetatable(luaBalanceTypeName)
 | 
			
		||||
	L.SetGlobal("balance", mt)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ import (
 | 
			
		||||
const luaDateTypeName = "date"
 | 
			
		||||
const timeFormat = "2006-01-02"
 | 
			
		||||
 | 
			
		||||
// Registers my date type to given L.
 | 
			
		||||
func luaRegisterDates(L *lua.LState) {
 | 
			
		||||
	mt := L.NewTypeMetatable(luaDateTypeName)
 | 
			
		||||
	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() {
 | 
			
		||||
	return {
 | 
			
		||||
		type: ErrorConstants.CLEAR_ERROR,
 | 
			
		||||
@@ -30,5 +41,6 @@ function clearError() {
 | 
			
		||||
module.exports = {
 | 
			
		||||
	serverError: serverError,
 | 
			
		||||
	ajaxError: ajaxError,
 | 
			
		||||
	clientError: clientError,
 | 
			
		||||
	clearError: clearError
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,10 @@ var models = require('../models.js');
 | 
			
		||||
var Report = models.Report;
 | 
			
		||||
var Error = models.Error;
 | 
			
		||||
 | 
			
		||||
function fetchReport() {
 | 
			
		||||
function fetchReport(reportName) {
 | 
			
		||||
	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) {
 | 
			
		||||
	return function (dispatch) {
 | 
			
		||||
		dispatch(fetchReport());
 | 
			
		||||
		dispatch(fetchReport(report));
 | 
			
		||||
 | 
			
		||||
		$.ajax({
 | 
			
		||||
			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 = {
 | 
			
		||||
	fetch: fetch
 | 
			
		||||
	fetch: fetch,
 | 
			
		||||
	select: select
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
var React = require('react');
 | 
			
		||||
 | 
			
		||||
var ReactBootstrap = require('react-bootstrap');
 | 
			
		||||
 | 
			
		||||
var Button = ReactBootstrap.Button;
 | 
			
		||||
var Panel = ReactBootstrap.Panel;
 | 
			
		||||
 | 
			
		||||
var StackedBarChart = require('../components/StackedBarChart');
 | 
			
		||||
 | 
			
		||||
module.exports = React.createClass({
 | 
			
		||||
@@ -10,10 +15,69 @@ module.exports = React.createClass({
 | 
			
		||||
	componentWillMount: function() {
 | 
			
		||||
		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() {
 | 
			
		||||
		report = [];
 | 
			
		||||
		if (this.props.reports['monthly_expenses'])
 | 
			
		||||
			report = (<StackedBarChart data={this.props.reports['monthly_expenses']} />);
 | 
			
		||||
		var report = [];
 | 
			
		||||
		if (this.props.selectedReport.report) {
 | 
			
		||||
			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 (
 | 
			
		||||
			<div>
 | 
			
		||||
				{report}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,13 @@
 | 
			
		||||
var d3 = require('d3');
 | 
			
		||||
var React = require('react');
 | 
			
		||||
 | 
			
		||||
var Panel = require('react-bootstrap').Panel;
 | 
			
		||||
 | 
			
		||||
module.exports = React.createClass({
 | 
			
		||||
	displayName: "StackedBarChart",
 | 
			
		||||
	calcMinMax: function(data) {
 | 
			
		||||
	calcMinMax: function(series) {
 | 
			
		||||
		var children = [];
 | 
			
		||||
		for (var child in data) {
 | 
			
		||||
			if (data.hasOwnProperty(child))
 | 
			
		||||
				children.push(data[child]);
 | 
			
		||||
		for (var child in series) {
 | 
			
		||||
			if (series.hasOwnProperty(child))
 | 
			
		||||
				children.push(series[child]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var positiveValues = [0];
 | 
			
		||||
@@ -40,12 +38,6 @@ module.exports = React.createClass({
 | 
			
		||||
		return Math.ceil(rangePerTick/roundTo)*roundTo;
 | 
			
		||||
	},
 | 
			
		||||
	render: function() {
 | 
			
		||||
		var data = this.props.data.mapReduceChildren(null,
 | 
			
		||||
			function(accumulator, currentValue, currentIndex, array) {
 | 
			
		||||
				return accumulator + currentValue;
 | 
			
		||||
			}
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		height = 400;
 | 
			
		||||
		width = 600;
 | 
			
		||||
		legendWidth = 100;
 | 
			
		||||
@@ -54,7 +46,7 @@ module.exports = React.createClass({
 | 
			
		||||
		height -= yMargin*2;
 | 
			
		||||
		width -= xMargin*2;
 | 
			
		||||
 | 
			
		||||
		var minMax = this.calcMinMax(data);
 | 
			
		||||
		var minMax = this.calcMinMax(this.props.report.FlattenedSeries);
 | 
			
		||||
		var y = d3.scaleLinear()
 | 
			
		||||
			.range([0, height])
 | 
			
		||||
			.domain(minMax);
 | 
			
		||||
@@ -63,7 +55,7 @@ module.exports = React.createClass({
 | 
			
		||||
 | 
			
		||||
		var x = d3.scaleLinear()
 | 
			
		||||
			.range([0, width])
 | 
			
		||||
			.domain([0, this.props.data.Labels.length + 0.5]);
 | 
			
		||||
			.domain([0, this.props.report.Labels.length + 0.5]);
 | 
			
		||||
 | 
			
		||||
		var bars = [];
 | 
			
		||||
		var labels = [];
 | 
			
		||||
@@ -76,13 +68,13 @@ module.exports = React.createClass({
 | 
			
		||||
		// negativeSum arrays
 | 
			
		||||
		var positiveSum = [];
 | 
			
		||||
		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);
 | 
			
		||||
			negativeSum.push(0);
 | 
			
		||||
			var labelX = x(i) + barStart + barWidth/2;
 | 
			
		||||
			var labelY = height + 15;
 | 
			
		||||
			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((
 | 
			
		||||
				<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)
 | 
			
		||||
			makeXLabel(i);
 | 
			
		||||
 | 
			
		||||
		//TODO handle Values from current series?
 | 
			
		||||
		var legendMap = {};
 | 
			
		||||
		for (var child in data) {
 | 
			
		||||
			childId++;
 | 
			
		||||
			var rectClasses = "chart-element chart-color" + (childId % 12);
 | 
			
		||||
			if (data.hasOwnProperty(child)) {
 | 
			
		||||
				for (var i=0; i < data[child].length; i++) {
 | 
			
		||||
					var value = data[child][i];
 | 
			
		||||
		for (var child in this.props.report.FlattenedSeries) {
 | 
			
		||||
			if (this.props.report.FlattenedSeries.hasOwnProperty(child)) {
 | 
			
		||||
				childId++;
 | 
			
		||||
				var childData = this.props.report.FlattenedSeries[child];
 | 
			
		||||
				var rectClasses = "chart-element chart-color" + (childId % 12);
 | 
			
		||||
				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)
 | 
			
		||||
						continue;
 | 
			
		||||
					legendMap[child] = childId;
 | 
			
		||||
@@ -124,10 +126,15 @@ module.exports = React.createClass({
 | 
			
		||||
						negativeSum[i] += rectHeight;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					bars.push((
 | 
			
		||||
						<rect className={rectClasses} x={x(i) + barStart} y={rectY} width={barWidth} height={rectHeight} rx={1} ry={1}/>
 | 
			
		||||
					seriesBars.push((
 | 
			
		||||
						<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 (
 | 
			
		||||
			<Panel header={this.props.data.Title}>
 | 
			
		||||
				<svg height={height + 2*yMargin} width={width + 2*xMargin + legendWidth}>
 | 
			
		||||
					<g className="stacked-bar-chart" transform={"translate("+xMargin+" "+yMargin+")"}>
 | 
			
		||||
						{bars}
 | 
			
		||||
						<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} />
 | 
			
		||||
						{labels}
 | 
			
		||||
					</g>
 | 
			
		||||
					<g className="chart-legend" transform={"translate("+(width + 2*xMargin)+" "+yMargin+")"}>
 | 
			
		||||
						{legend}
 | 
			
		||||
					</g>
 | 
			
		||||
				</svg>
 | 
			
		||||
			</Panel>
 | 
			
		||||
			<svg height={height + 2*yMargin} width={width + 2*xMargin + legendWidth}>
 | 
			
		||||
				<g className="stacked-bar-chart" transform={"translate("+xMargin+" "+yMargin+")"}>
 | 
			
		||||
					{bars}
 | 
			
		||||
					<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} />
 | 
			
		||||
					{labels}
 | 
			
		||||
				</g>
 | 
			
		||||
				<g className="chart-legend" transform={"translate("+(width + 2*xMargin)+" "+yMargin+")"}>
 | 
			
		||||
					{legend}
 | 
			
		||||
				</g>
 | 
			
		||||
			</svg>
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -2,5 +2,7 @@ var keyMirror = require('keymirror');
 | 
			
		||||
 | 
			
		||||
module.exports = keyMirror({
 | 
			
		||||
	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) {
 | 
			
		||||
	return {
 | 
			
		||||
		reports: state.reports
 | 
			
		||||
		reports: state.reports,
 | 
			
		||||
		selectedReport: state.selectedReport
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mapDispatchToProps(dispatch) {
 | 
			
		||||
	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() {
 | 
			
		||||
	this.Values = [];
 | 
			
		||||
	this.Children = {};
 | 
			
		||||
	this.Series = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Series.prototype.toJSONobj = function() {
 | 
			
		||||
	var json_obj = {};
 | 
			
		||||
	json_obj.Values = this.Values;
 | 
			
		||||
	json_obj.Children = {};
 | 
			
		||||
	for (var child in this.Children) {
 | 
			
		||||
		if (this.Children.hasOwnProperty(child))
 | 
			
		||||
			json_obj.Children[child] = this.Children[child].toJSONobj();
 | 
			
		||||
	json_obj.Series = {};
 | 
			
		||||
	for (var child in this.Series) {
 | 
			
		||||
		if (this.Series.hasOwnProperty(child))
 | 
			
		||||
			json_obj.Series[child] = this.Series[child].toJSONobj();
 | 
			
		||||
	}
 | 
			
		||||
	return json_obj;
 | 
			
		||||
}
 | 
			
		||||
@@ -416,20 +416,20 @@ Series.prototype.toJSONobj = function() {
 | 
			
		||||
Series.prototype.fromJSONobj = function(json_obj) {
 | 
			
		||||
	if (json_obj.hasOwnProperty("Values"))
 | 
			
		||||
		this.Values = json_obj.Values;
 | 
			
		||||
	if (json_obj.hasOwnProperty("Children")) {
 | 
			
		||||
		for (var child in json_obj.Children) {
 | 
			
		||||
			if (json_obj.Children.hasOwnProperty(child))
 | 
			
		||||
				this.Children[child] = new Series();
 | 
			
		||||
				this.Children[child].fromJSONobj(json_obj.Children[child]);
 | 
			
		||||
	if (json_obj.hasOwnProperty("Series")) {
 | 
			
		||||
		for (var child in json_obj.Series) {
 | 
			
		||||
			if (json_obj.Series.hasOwnProperty(child))
 | 
			
		||||
				this.Series[child] = new Series();
 | 
			
		||||
				this.Series[child].fromJSONobj(json_obj.Series[child]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Series.prototype.mapReduceChildren = function(mapFn, reduceFn) {
 | 
			
		||||
	var children = {}
 | 
			
		||||
	for (var child in this.Children) {
 | 
			
		||||
		if (this.Children.hasOwnProperty(child))
 | 
			
		||||
			children[child] = this.Children[child].mapReduce(mapFn, reduceFn);
 | 
			
		||||
	for (var child in this.Series) {
 | 
			
		||||
		if (this.Series.hasOwnProperty(child))
 | 
			
		||||
			children[child] = this.Series[child].mapReduce(mapFn, reduceFn);
 | 
			
		||||
	}
 | 
			
		||||
	return children;
 | 
			
		||||
}
 | 
			
		||||
@@ -441,9 +441,9 @@ Series.prototype.mapReduce = function(mapFn, reduceFn) {
 | 
			
		||||
	else
 | 
			
		||||
		childValues.push(this.Values.slice());
 | 
			
		||||
 | 
			
		||||
	for (var child in this.Children) {
 | 
			
		||||
		if (this.Children.hasOwnProperty(child))
 | 
			
		||||
			childValues.push(this.Children[child].mapReduce(mapFn, reduceFn));
 | 
			
		||||
	for (var child in this.Series) {
 | 
			
		||||
		if (this.Series.hasOwnProperty(child))
 | 
			
		||||
			childValues.push(this.Series[child].mapReduce(mapFn, reduceFn));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var reducedValues = [];
 | 
			
		||||
@@ -466,8 +466,11 @@ function Report() {
 | 
			
		||||
	this.YAxisLabel = "";
 | 
			
		||||
	this.Labels = [];
 | 
			
		||||
	this.Series = {};
 | 
			
		||||
	this.FlattenedSeries = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Report.prototype.topLevelAccountName = "(top level)";
 | 
			
		||||
 | 
			
		||||
Report.prototype.toJSON = function() {
 | 
			
		||||
	var json_obj = {};
 | 
			
		||||
	json_obj.ReportId = this.ReportId;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ var SecurityTemplateReducer = require('./SecurityTemplateReducer');
 | 
			
		||||
var SelectedAccountReducer = require('./SelectedAccountReducer');
 | 
			
		||||
var SelectedSecurityReducer = require('./SelectedSecurityReducer');
 | 
			
		||||
var ReportReducer = require('./ReportReducer');
 | 
			
		||||
var SelectedReportReducer = require('./SelectedReportReducer');
 | 
			
		||||
var ErrorReducer = require('./ErrorReducer');
 | 
			
		||||
 | 
			
		||||
module.exports = Redux.combineReducers({
 | 
			
		||||
@@ -19,5 +20,6 @@ module.exports = Redux.combineReducers({
 | 
			
		||||
	selectedAccount: SelectedAccountReducer,
 | 
			
		||||
	selectedSecurity: SelectedSecurityReducer,
 | 
			
		||||
	reports: ReportReducer,
 | 
			
		||||
	selectedReport: SelectedReportReducer,
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
type Series struct {
 | 
			
		||||
	Values   []float64
 | 
			
		||||
	Children map[string]*Series
 | 
			
		||||
	Values []float64
 | 
			
		||||
	Series map[string]*Series
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Report struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -97,8 +97,8 @@ func luaReportSeries(L *lua.LState) int {
 | 
			
		||||
		ud.Value = s
 | 
			
		||||
	} else {
 | 
			
		||||
		report.Series[name] = &Series{
 | 
			
		||||
			Children: make(map[string]*Series),
 | 
			
		||||
			Values:   make([]float64, cap(report.Labels)),
 | 
			
		||||
			Series: make(map[string]*Series),
 | 
			
		||||
			Values: make([]float64, cap(report.Labels)),
 | 
			
		||||
		}
 | 
			
		||||
		ud.Value = report.Series[name]
 | 
			
		||||
	}
 | 
			
		||||
@@ -157,8 +157,8 @@ func luaSeries__index(L *lua.LState) int {
 | 
			
		||||
	switch field {
 | 
			
		||||
	case "Value", "value":
 | 
			
		||||
		L.Push(L.NewFunction(luaSeriesValue))
 | 
			
		||||
	case "Series", "series", "Child", "child":
 | 
			
		||||
		L.Push(L.NewFunction(luaSeriesChildren))
 | 
			
		||||
	case "Series", "series":
 | 
			
		||||
		L.Push(L.NewFunction(luaSeriesSeries))
 | 
			
		||||
	default:
 | 
			
		||||
		L.ArgError(2, "unexpected series attribute: "+field)
 | 
			
		||||
	}
 | 
			
		||||
@@ -179,20 +179,20 @@ func luaSeriesValue(L *lua.LState) int {
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func luaSeriesChildren(L *lua.LState) int {
 | 
			
		||||
func luaSeriesSeries(L *lua.LState) int {
 | 
			
		||||
	parent := luaCheckSeries(L, 1)
 | 
			
		||||
	name := L.CheckString(2)
 | 
			
		||||
	ud := L.NewUserData()
 | 
			
		||||
 | 
			
		||||
	s, ok := parent.Children[name]
 | 
			
		||||
	s, ok := parent.Series[name]
 | 
			
		||||
	if ok {
 | 
			
		||||
		ud.Value = s
 | 
			
		||||
	} else {
 | 
			
		||||
		parent.Children[name] = &Series{
 | 
			
		||||
			Children: make(map[string]*Series),
 | 
			
		||||
			Values:   make([]float64, cap(parent.Values)),
 | 
			
		||||
		parent.Series[name] = &Series{
 | 
			
		||||
			Series: make(map[string]*Series),
 | 
			
		||||
			Values: make([]float64, cap(parent.Values)),
 | 
			
		||||
		}
 | 
			
		||||
		ud.Value = parent.Children[name]
 | 
			
		||||
		ud.Value = parent.Series[name]
 | 
			
		||||
	}
 | 
			
		||||
	L.SetMetatable(ud, L.GetTypeMetatable(luaSeriesTypeName))
 | 
			
		||||
	L.Push(ud)
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,6 @@ func luaGetSecurities(L *lua.LState) int {
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registers my security type to given L.
 | 
			
		||||
func luaRegisterSecurities(L *lua.LState) {
 | 
			
		||||
	mt := L.NewTypeMetatable(luaSecurityTypeName)
 | 
			
		||||
	L.SetGlobal("security", mt)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@
 | 
			
		||||
}
 | 
			
		||||
.chart-color8 {
 | 
			
		||||
	fill: #ff7f00;
 | 
			
		||||
	fill: #df5f00;
 | 
			
		||||
	stroke: #df5f00;
 | 
			
		||||
}
 | 
			
		||||
.chart-color9 {
 | 
			
		||||
	fill: #cab2d6;
 | 
			
		||||
@@ -50,7 +50,7 @@
 | 
			
		||||
.chart-element {
 | 
			
		||||
	stroke-width: 0;
 | 
			
		||||
}
 | 
			
		||||
.chart-element:hover {
 | 
			
		||||
g.chart-series:hover .chart-element {
 | 
			
		||||
	stroke-width: 2;
 | 
			
		||||
}
 | 
			
		||||
.chart-legend rect {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user