Fun with Highcharts, Part II

“The only thing that endures over time is the Law of the Farm. You must prepare the ground, plant the seed, cultivate, and water it if you expect to reap the harvest.”
Stephen Covey

Now that I have my functioning generic chart widget, it’s time to start working on my chart object generator. My intent, as usual, is to start out small and then build up a library of various chart types over time. Conceptually, I want to create a utility function to which I can pass some chart data and a chart type, and have it return a fully configured chart object that I can pass to my generic chart widget which will then render the chart. In use, it would look something like this:

var chartObject = generator.getChartObject(chartData, chartType);

Theoretically, you could make the chartType parameter optional if you set up a default, and then you would only have to pass that in when you wanted something other than the the established standard. For my first chart, though, I think I will do something slightly more sophisticated, mainly because this is the kind of thing that I like to utilize when I’m looking at the distribution of work within and across teams. I call it a Workload Chart, and it’s just your typical three-value tracking of work coming in, work getting done, and work still left in the needs-to-get-done pile. It doesn’t matter what the work is, or who is doing it, or over what period you are tracking things — the concept is pretty universal and the chart is the chart, regardless of the underlying data. Here’s one that allows you to select the Team, the Type of work, the Frequency, and the Period from a series of drop-down selections:

Typical workload chart

The chart data is the stuff that will change: title, subtitle, data values, and the labels across the bottom. The chart object includes all of the chart data, plus all of those structural elements that are constant and independent of the data. The utility function will take in the chart data, add it to a predefined model, and then return the entire object back to the caller. Here is the basic structure of the Script Include, which I called the GenericChartUtil:

var GenericChartUtil = Class.create();
GenericChartUtil.prototype = {
	initialize: function() {
	},

	getChartObject: function(chartData, chartType) {
		var chartObject = {};

		if (chartType == 'workload') {
			chartObject = this._getWorkloadChart(chartData);
		} else if (chartType == 'bar') {
			chartObject = this._getBarChart(chartData);
		} else {
			chartObject = this._getPieChart(chartData);
		}

		return chartObject;
	},

	_getPieChart: function(chartData) {
		return {
//			type-specific chart object w/data
		};
	},

	_getBarChart: function(chartData) {
		return {
//			type-specific chart object w/data
		};
	},

	_getWorkloadChart: function(chartData) {
		return {
//			type-specific chart object w/data
		};
	},

	type: 'GenericChartUtil'
};

For the workload chart, the dynamic data includes the title, the subtitle, an array of values for the received bars , an array of values for the completed bars , an array of values for the remaining work trend line (backlog), and an array of values for the time period labels. For the chart displayed in the previous image, the chartData object might look something like this:

{
   "title":"Incidents assigned to Hardware",
   "subtitle":"Quarterly through 9/30/2019",
   "labels":[
      "12/31/2017",
      "3/31/2018",
      "6/30/2018",
      "9/30/2018",
      "12/31/2018",
      "3/31/2019",
      "6/30/2019",
      "9/30/2019"
   ],
   "received":[0, 0, 7, 2, 0, 0, 0, 1],
   "completed":[0, 0, 6, 1, 0, 0, 0, 0],
   "backlog":[0, 0, 1, 2, 2, 2, 2, 3]
}

How you would compile that information is a subject for another time; today we just want to focus on how our chart object generator would take that information in and use it to create a complete chart object to be passed to Highcharts. It’s actually quite simple to do, and can be accomplished with a simple return statement that sends back a Javascript object that is a combination of hard-coded values and data pulled from the incoming chart data object. Here is the completed function for the workload chart:

	_getWorkloadChart: function(chartData) {
		return {
			chart: {
				zoomType: "xy"
			},
			title: {
				text: chartData.title
			},
			subtitle: {
				text: chartData.subtitle
			},
			xAxis: [
				{
					categories: chartData.labels,
					crosshair: true
				}
			],
			yAxis: [
				{
					labels: {
						format: "{value}",
						style: {}
					},
					title: {
						text: "Recieved/Completed",
						style: {}
					}
				},{
					labels: {
						format: "{value}",
						style: {}
					},
					title: {
						text: "Backlog",
						style: {}
					},
					opposite: true
				}
			],
			tooltip: {
				shared: true
			},
			legend: {
				layout: "vertical",
				align: "left",
				x: 120,
				verticalAlign: "top",
				y: 100,
				floating: true,
				backgroundColor: "rgba(255,255,255,0.25)"
			},
			series: [
				{
					name: "Received",
					type: "column",
					data: chartData.received,
					tooltip: {
						valueSuffix: " received"
					}
				},{
					name: "Completed",
					type: "column",
					data: chartData.completed,
					tooltip: {
						valueSuffix: " completed"
					}
				},{
					name: "Backlog",
					type: "spline",
					yAxis: 1,
					data: chartData.backlog,
					tooltip: {
						valueSuffix: " remaining"
					}
				}
			]
		};
	},

For every property in the returned chart object, there is either a hard-coded constant value that is an element of the chart template/design, or there is data pulled from the incoming chart data object. This idea can be repeated for any type or design of chart desired, and you can make independent decisions on a chart by chart basis as to what is a standard, fixed value and what has to be provided in the chart data object. As you get more sophisticated, you can even set up default values for certain things that can be overridden by values present in the chart data. If there is a value in the chart data, you can use that; otherwise, you can fall back to the predetermined default value. But before we start making more chart types, we’ll need to figure out how to obtain all of the values for the chart data object to support our workload chart example. That sounds like a bit of work though, so I think we should just call that a great topic for a future installment