Fun with Highcharts, Part IV

“Lost wealth may be replaced by industry, lost knowledge by study, lost health by temperance or medicine, but lost time is gone forever.”
Samuel Smiles

Now that we have our generic chart widget, our generic chart object generator, and the choice lists and default selections for our example chart, we just need to come up with the code to gather up the data for the chart based on the selections. A number of elements are based on the period selected, so collecting that data would seem like an important first step. I organized all of that into a function that I called getPeriodData:

function getPeriodData() {
	var periodData = {};
	periodData.frequencyInfo = findOption(data.config.freqOptions, data.frequency);
	periodData.endingDateInfo = findOption(data.config.endingOptions[data.frequency], data.ending);
	periodData.labels = [];
	periodData.endDate = [];
	for (var i=0; i<=periodData.frequencyInfo.size; i++) {
		var dt = new Date(periodData.endingDateInfo.label);
		if (data.frequency == 'd') {
			dt.setDate(dt.getDate() - (periodData.frequencyInfo.size - i));
		} else if (data.frequency == 'w') {
			dt.setDate(dt.getDate() - (periodData.frequencyInfo.size - i) * 7);
		} else if (data.frequency == 'm') {
			dt.setDate(1);
			dt.setMonth(dt.getMonth() - (periodData.frequencyInfo.size - i));
			dt = getLastDayOfMonth(dt);
		} else if (data.frequency == 'q') {
			dt.setDate(1);
			dt.setMonth(dt.getMonth() - (periodData.frequencyInfo.size - i) * 3);
			dt = getLastDayOfMonth(dt);
		} else if (data.frequency == 'y') {
			dt.setFullYear(dt.getFullYear() - (periodData.frequencyInfo.size - i));
		}
		var dtInfo = getDateValues(dt);
		periodData.endDate.push(dtInfo.value);
		if (i > 0) {
			periodData.labels.push(dtInfo.label);
		}
	}			
		
	return periodData;
}

The function collects one more end date than it does labels because the end date of the previous period is used as the start of the current period. You need to be able to go back one extra period to get the end date of a period that you will not actually be using for the start date of the earliest period that you will.

Once you pull together all of the data for the selected frequency and period, you can then use that data to put together everything else needed for the chart. This takes care of the basics:

var chartData = {};
chartData.title = task.getPlural() + ' assigned to ' + findOption(data.config.groupOptions, data.group).label;
chartData.subtitle = periodData.frequencyInfo.label + ' through ' + periodData.endingDateInfo.label;
chartData.labels = periodData.labels;
chartData.received = [];
chartData.completed = [];
chartData.backlog = [];

… and then all that is left is to loop through all of the periods in the chart to run GlideAggregates to compile all of the actual data:

var task = new GlideAggregate(data.type);
for (var i=1; i<periodData.endDate.length; i++) {
	task.initialize();
	task.addAggregate('COUNT');
	task.addEncodedQuery('assignment_group=' + data.group + '^opened_at>' + periodData.endDate[i-1] + '^opened_at<=' + periodData.endDate[i]);
	task.query();
	task.next();
	chartData.received.push(task.getAggregate('COUNT') * 1);
	task.initialize();
	task.addAggregate('COUNT');
	task.addEncodedQuery('assignment_group=' + data.group + '^closed_at>' + periodData.endDate[i-1] + '^closed_at<=' + periodData.endDate[i]);
	task.query();
	task.next();
	chartData.completed.push(task.getAggregate('COUNT') * 1);
	task.initialize();
	task.addAggregate('COUNT');
	task.addEncodedQuery('assignment_group=' + data.group + '^opened_at<=' + periodData.endDate[i] + '^closed_at>' + periodData.endDate[i] + '^ORclosed_atISEMPTY');
	task.query();
	task.next();
	chartData.backlog.push(task.getAggregate('COUNT') * 1);
}

The last thing we need to deal with is the user making new selections from the four pick lists. That’s a client side issue, so we will need a client side script to detect the selections and call for a refresh of the chart.

function($scope, $rootScope) {
	var c = this;
	$scope.updateChart = function() {
		c.server.update().then(function(response) {
			c.data.config = response.config;
			c.data.group = response.group;
			c.data.type = response.type;
			c.data.frequency = response.frequency;
			c.data.ending = response.ending;
			$rootScope.$broadcast('refresh-workload', {chartObject: response.chartObject});
		});
	}
}

I still want to play around with a few more different and interesting chart types, but there are enough parts and pieces now to warrant the assembly of a version 1.0 Update Set. If I ever get a chance to do more, I can always put out a better one later on.