From 9c1b0674459e78cb269d16ad373b3b9ae93ca028 Mon Sep 17 00:00:00 2001
From: Julian Lam <julian@designcreateplay.com>
Date: Wed, 25 Nov 2015 14:22:32 -0500
Subject: [PATCH] Closes #3884

Added toggles to change graph to monthly view vs regular
single-day view.

Also fixed issue where labels were out of date as the graph
data changed.
---
 public/src/admin/general/dashboard.js | 52 ++++++++++++++++++++++-----
 src/socket.io/admin.js                | 52 ++++++++++++++++++++++-----
 src/views/admin/general/dashboard.tpl |  4 +--
 3 files changed, 90 insertions(+), 18 deletions(-)

diff --git a/public/src/admin/general/dashboard.js b/public/src/admin/general/dashboard.js
index 62280f2a79..08f47c9a22 100644
--- a/public/src/admin/general/dashboard.js
+++ b/public/src/admin/general/dashboard.js
@@ -150,12 +150,26 @@ define('admin/general/dashboard', ['semver'], function(semver) {
 
 		for (var i = currentHour, ii = currentHour - 24; i > ii; i--) {
 			var hour = i < 0 ? 24 + i : i;
-			labels.push(hour + ':00 ');
+			labels.push(hour + ':00');
 		}
 
 		return labels.reverse();
 	}
 
+	function getDaysArray() {
+		var currentDay = new Date().getTime(),
+			months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+			labels = [],
+			tmpDate;
+
+		for(var x=29;x>=0;x--) {
+			tmpDate = new Date(currentDay - (1000*60*60*24*x));
+			labels.push(months[tmpDate.getMonth()] + ' ' + tmpDate.getDate());
+		}
+
+		return labels;
+	}
+
 	function setupGraphs() {
 		var trafficCanvas = document.getElementById('analytics-traffic'),
 			registeredCanvas = document.getElementById('analytics-registered'),
@@ -263,6 +277,10 @@ define('admin/general/dashboard', ['semver'], function(semver) {
 
 		$(window).on('resize', adjustPieCharts);
 		adjustPieCharts();
+
+		$('[data-action="updateGraph"]').on('click', function() {
+			updateTrafficGraph($(this).attr('data-units'));
+		})
 	}
 
 	function adjustPieCharts() {
@@ -277,26 +295,44 @@ define('admin/general/dashboard', ['semver'], function(semver) {
 		});
 	}
 
-	function updateTrafficGraph() {
+	function updateTrafficGraph(units) {
 		if (!app.isFocused) {
 			return;
 		}
 
-		socket.emit('admin.analytics.get', {graph: "traffic"}, function (err, data) {
+		socket.emit('admin.analytics.get', {
+			graph: 'traffic',
+			units: units || 'hours'
+		}, function (err, data) {
 			if (JSON.stringify(graphData.traffic) === JSON.stringify(data)) {
 				return;
 			}
 
 			graphData.traffic = data;
 
-			for (var i = 0, ii = data.pageviews.length; i < ii;  i++) {
-				graphs.traffic.datasets[0].points[i].value = data.pageviews[i];
-				graphs.traffic.datasets[1].points[i].value = data.uniqueVisitors[i];
+			// If new data set contains fewer points than currently shown, truncate
+			while(graphs.traffic.datasets[0].points.length > data.pageviews.length) {
+				graphs.traffic.removeData();
 			}
 
-			var currentHour = new Date().getHours();
+			if (units === 'days') {
+				graphs.traffic.scale.xLabels = getDaysArray();
+			} else {
+				graphs.traffic.scale.xLabels = getHoursArray();
+			}
+
+			for (var i = 0, ii = data.pageviews.length; i < ii;  i++) {
+				if (graphs.traffic.datasets[0].points[i]) {
+					graphs.traffic.datasets[0].points[i].value = data.pageviews[i];
+					graphs.traffic.datasets[0].points[i].label = graphs.traffic.scale.xLabels[i];
+					graphs.traffic.datasets[1].points[i].value = data.uniqueVisitors[i];
+					graphs.traffic.datasets[1].points[i].label = graphs.traffic.scale.xLabels[i];
+				} else {
+					// No points to replace? Add data.
+					graphs.traffic.addData([data.pageviews[i], data.uniqueVisitors[i]], graphs.traffic.scale.xLabels[i]);
+				}
+			}
 
-			graphs.traffic.scale.xLabels = getHoursArray();
 			graphs.traffic.update();
 
 			$('#pageViewsThisMonth').html(data.monthlyPageViews.thisMonth);
diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js
index d5ada9d4e0..df00925dad 100644
--- a/src/socket.io/admin.js
+++ b/src/socket.io/admin.js
@@ -206,17 +206,29 @@ SocketAdmin.email.test = function(socket, data, callback) {
 };
 
 SocketAdmin.analytics.get = function(socket, data, callback) {
-	data.units = 'hours'; // temp
-	data.amount = 24;
+	// Default returns views from past 24 hours, by hour
+	if (data.units === 'days') {
+		data.amount = 30;
+	} else {
+		data.amount = 24;
+	}
 
 	if (data && data.graph && data.units && data.amount) {
 		if (data.graph === 'traffic') {
 			async.parallel({
 				uniqueVisitors: function(next) {
-					getHourlyStatsForSet('analytics:uniquevisitors', data.amount, next);
+					if (data.units === 'days') {
+						getDailyStatsForSet('analytics:uniquevisitors', Date.now(), data.amount, next);
+					} else {
+						getHourlyStatsForSet('analytics:uniquevisitors', Date.now(), data.amount, next);
+					}
 				},
 				pageviews: function(next) {
-					getHourlyStatsForSet('analytics:pageviews', data.amount, next);
+					if (data.units === 'days') {
+						getDailyStatsForSet('analytics:pageviews', Date.now(), data.amount, next);
+					} else {
+						getHourlyStatsForSet('analytics:pageviews', Date.now(), data.amount, next);
+					}
 				},
 				monthlyPageViews: function(next) {
 					analytics.getMonthlyPageViews(next);
@@ -240,14 +252,14 @@ SocketAdmin.logs.clear = function(socket, data, callback) {
 	meta.logs.clear(callback);
 };
 
-function getHourlyStatsForSet(set, hours, callback) {
-	var hour = new Date(),
-		terms = {},
+function getHourlyStatsForSet(set, hour, numHours, callback) {
+	var terms = {},
 		hoursArr = [];
 
+	hour = new Date(hour);
 	hour.setHours(hour.getHours(), 0, 0, 0);
 
-	for (var i = 0, ii = hours; i < ii; i++) {
+	for (var i = 0, ii = numHours; i < ii; i++) {
 		hoursArr.push(hour.getTime());
 		hour.setHours(hour.getHours() - 1, 0, 0, 0);
 	}
@@ -272,6 +284,30 @@ function getHourlyStatsForSet(set, hours, callback) {
 	});
 }
 
+function getDailyStatsForSet(set, day, numDays, callback) {
+	var daysArr = [];
+
+	day = new Date(day);
+	day.setHours(0, 0, 0, 0);
+
+	async.whilst(function() {
+		return numDays--;
+	}, function(next) {
+		getHourlyStatsForSet(set, day.getTime()-(1000*60*60*24*numDays), 24, function(err, day) {
+			if (err) {
+				return next(err);
+			}
+
+			daysArr.push(day.reduce(function(cur, next) {
+				return cur+next;
+			}));
+			next();
+		});
+	}, function(err) {
+		callback(err, daysArr);
+	});
+};
+
 SocketAdmin.getMoreEvents = function(socket, next, callback) {
 	var start = parseInt(next, 10);
 	if (start < 0) {
diff --git a/src/views/admin/general/dashboard.tpl b/src/views/admin/general/dashboard.tpl
index 3aa18f8867..153dd643be 100644
--- a/src/views/admin/general/dashboard.tpl
+++ b/src/views/admin/general/dashboard.tpl
@@ -17,11 +17,11 @@
 				</div>
 				<div class="text-center pull-left monthly-pageviews">
 					<div><strong id="pageViewsThisMonth"></strong></div>
-					<div>Page views This Month</div>
+					<div><a href="#" data-action="updateGraph" data-units="days">Page views This Month</a></div>
 				</div>
 				<div class="text-center pull-left monthly-pageviews">
 					<div><strong id="pageViewsPastDay"></strong></div>
-					<div>Page views in last 24 hours</div>
+					<div><a href="#" data-action="updateGraph" data-units="hours">Page views in last 24 hours</a></div>
 				</div>
 			</div>
 		</div>