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
 | 
						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 {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user