From 2e79e8421cd9e1b74cebd277e43fadde908b23f0 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 6 Nov 2012 10:54:52 -0500 Subject: [PATCH 1/3] CC-4661: Listener Statistics - better legend for multiple series of data - consistant number of ticks on x-axis --- .../application/models/ListenerStat.php | 11 +- .../views/scripts/listenerstat/index.phtml | 8 +- .../js/airtime/listenerstat/listenerstat.js | 101 +++++++++++++----- 3 files changed, 82 insertions(+), 38 deletions(-) diff --git a/airtime_mvc/application/models/ListenerStat.php b/airtime_mvc/application/models/ListenerStat.php index 903aa647c..a8ebd0f47 100644 --- a/airtime_mvc/application/models/ListenerStat.php +++ b/airtime_mvc/application/models/ListenerStat.php @@ -17,9 +17,14 @@ SQL; $out = array(); foreach ($data as $d) { + $t = new DateTime($d['timestamp'], new DateTimeZone("UTC")); + $t->setTimezone(new DateTimeZone(date_default_timezone_get())); + // tricking javascript so it thinks the server timezone is in UTC + $dt = new DateTime($t->format("Y-m-d H:i:s"), new DateTimeZone("UTC")); + + $d['timestamp'] = $dt->format("U"); $out[$d['mount_name']][] = $d; } - return $out; } @@ -43,11 +48,11 @@ SQL; $stats_sql = "INSERT INTO cc_listener_count (timestamp_id, listener_count, mount_name) VALUES (:timestamp_id, :listener_count, :mount_name)"; foreach ($p_dataPoints as $dp) { - $timestamp_id = Application_Common_Database::prepareAndExecute($timestamp_sql, + $timestamp_id = Application_Common_Database::prepareAndExecute($timestamp_sql, array('ts'=> $dp['timestamp']), "column"); Application_Common_Database::prepareAndExecute($stats_sql, - array('timestamp_id' => $timestamp_id, + array('timestamp_id' => $timestamp_id, 'listener_count' => $dp["num_listeners"], 'mount_name' => $dp["mount_name"], ) diff --git a/airtime_mvc/application/views/scripts/listenerstat/index.phtml b/airtime_mvc/application/views/scripts/listenerstat/index.phtml index af1c2c21d..3df9f08ca 100644 --- a/airtime_mvc/application/views/scripts/listenerstat/index.phtml +++ b/airtime_mvc/application/views/scripts/listenerstat/index.phtml @@ -1,13 +1,7 @@
Timestamp vs Listener Count
-
- +
date_form; ?>
\ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js b/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js index 3911dbad3..1b5c5393f 100644 --- a/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js +++ b/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js @@ -12,43 +12,91 @@ $(document).ready(function() { endTimestamp = AIRTIME.utilities.fnGetTimestamp(dateEndId, timeEndId); getDataAndPlot(startTimestamp, endTimestamp); }); - - listenerstat_content.find("#all_mps").change(function(){ - var mpName = $(this).val(); - startTimestamp = AIRTIME.utilities.fnGetTimestamp(dateStartId, timeStartId); - endTimestamp = AIRTIME.utilities.fnGetTimestamp(dateEndId, timeEndId); - getDataAndPlot(startTimestamp, endTimestamp); - getDataAndPlot(startTimestamp, endTimestamp, mpName); - }) }); -function getDataAndPlot(startTimestamp, endTimestamp, mountName){ +function getDataAndPlot(startTimestamp, endTimestamp){ // get data $.get('/Listenerstat/get-data', {startTimestamp: startTimestamp, endTimestamp: endTimestamp}, function(data){ data = JSON.parse(data); - out = new Array(); + out = new Object(); $.each(data, function(mpName, v){ - plotData = new Array(); - if (mountName != null && mpName != mountName){ - console.log(mountName); - return true; - } + plotData = new Object(); + plotData.data = new Array(); $.each(v, function(i, ele){ - temp = new Array(); - temp[0] = new Date(ele.timestamp.replace(/-/g,"/")); - temp[1] = ele.listener_count; - plotData.push(temp); + plotData.label = mpName; + var d = new Date(0); + d.setUTCSeconds(ele.timestamp); + plotData.data.push([d, ele.listener_count]); }) - out.push(plotData); + out[mpName] = plotData; }); - if (out.length == 0) { - out.push(new Array()); - } plot(out); }) } -function plot(d){ +function plot(datasets){ + data = null; + function plotByChoice(doAll) + { + // largest date object that you can set + firstTimestamp = new Date(8640000000000000); + // smallest + lastTimestamp = new Date(0); + + data = []; + if (doAll != null) + { + $.each(datasets, function(key, val) { + if (firstTimestamp.getTime() > val.data[0][0].getTime()) { + firstTimestamp = val.data[0][0]; + } + if (lastTimestamp.getTime() < val.data[val.data.length-1][0].getTime()) { + lastTimestamp = val.data[val.data.length-1][0]; + } + data.push(val); + }); + } + else + { + $('#legend .legendCB').each( + function(){ + if (this.checked) + { + data.push(datasets[this.id]); + } + else + { + data.push({label: this.id, data: []}) + } + } + ); + } + + numOfTicks = 10; + tickSize = (lastTimestamp.getTime() - firstTimestamp.getTime())/1000/numOfTicks; + + $.plot($("#flot_placeholder"), data, { + yaxis: { min: 0, tickDecimals: 0 }, + xaxis: { mode: "time", timeformat:"%y/%m/%0d %H:%M", tickSize: [tickSize, "second"] }, + legend: { + container: $('#legend'), + noColumns: 5, + labelFormatter: function (label, series) { + var cb = ' 0){ + cb += 'checked="true" '; + } + cb += 'id="'+label+'" /> '; + cb += label; + return cb; + } + } + }); + + $('#legend').find("input").click(function(){setTimeout(plotByChoice,100);}); + } + + plotByChoice(true); oBaseDatePickerSettings = { dateFormat: 'yy-mm-dd', onSelect: function(sDate, oDatePicker) { @@ -67,7 +115,4 @@ function plot(d){ listenerstat_content.find(timeStartId).timepicker(oBaseTimePickerSettings); listenerstat_content.find(dateEndId).datepicker(oBaseDatePickerSettings); listenerstat_content.find(timeEndId).timepicker(oBaseTimePickerSettings); - - $.plot($("#flot_placeholder"), d, { xaxis: { mode: "time", timeformat: "%y/%m/%0d %H:%M:%S" } }); - - } \ No newline at end of file +} \ No newline at end of file From 9712e64a6905e940babb34bc589b10b79b6368b3 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 6 Nov 2012 14:52:35 -0500 Subject: [PATCH 2/3] CC-4661: Listener Statistics - bug fix and removing unnecessary code --- .../application/controllers/ListenerstatController.php | 3 --- airtime_mvc/public/js/airtime/listenerstat/listenerstat.js | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/airtime_mvc/application/controllers/ListenerstatController.php b/airtime_mvc/application/controllers/ListenerstatController.php index d0af81593..d80bcf871 100644 --- a/airtime_mvc/application/controllers/ListenerstatController.php +++ b/airtime_mvc/application/controllers/ListenerstatController.php @@ -46,9 +46,6 @@ class ListenerstatController extends Zend_Controller_Action 'his_time_end' => $end->format("H:i") )); - $allMPs = Application_Model_ListenerStat::getAllMPNames(); - - $this->view->mps = $allMPs; $this->view->date_form = $form; } diff --git a/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js b/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js index 1b5c5393f..02ebefab8 100644 --- a/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js +++ b/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js @@ -63,6 +63,12 @@ function plot(datasets){ if (this.checked) { data.push(datasets[this.id]); + if (firstTimestamp.getTime() > datasets[this.id].data[0][0].getTime()) { + firstTimestamp = datasets[this.id].data[0][0]; + } + if (lastTimestamp.getTime() < datasets[this.id].data[datasets[this.id].data.length-1][0].getTime()) { + lastTimestamp = datasets[this.id].data[datasets[this.id].data.length-1][0]; + } } else { From 9112a8d0b11dbb66511ca6f681ddb03124989b05 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 7 Nov 2012 16:25:59 -0500 Subject: [PATCH 3/3] CC-4661: Listener Statistics - enhancement on flot - data point reduction --- .../application/configs/navigation.php | 2 +- .../controllers/ListenerstatController.php | 1 + .../application/models/ListenerStat.php | 39 ++++++++++---- .../views/scripts/listenerstat/index.phtml | 2 +- .../js/airtime/listenerstat/listenerstat.js | 53 ++++++++++++++++++- 5 files changed, 84 insertions(+), 13 deletions(-) diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php index e2df2e3f5..59ce874fe 100644 --- a/airtime_mvc/application/configs/navigation.php +++ b/airtime_mvc/application/configs/navigation.php @@ -87,7 +87,7 @@ $pages = array( 'resource' => 'playouthistory' ), array( - 'label' => 'Listener Stat', + 'label' => 'Listener Stats', 'module' => 'default', 'controller' => 'listenerstat', 'action' => 'index', diff --git a/airtime_mvc/application/controllers/ListenerstatController.php b/airtime_mvc/application/controllers/ListenerstatController.php index d80bcf871..648a88dfe 100644 --- a/airtime_mvc/application/controllers/ListenerstatController.php +++ b/airtime_mvc/application/controllers/ListenerstatController.php @@ -18,6 +18,7 @@ class ListenerstatController extends Zend_Controller_Action $baseUrl = $request->getBaseUrl(); $this->view->headScript()->appendFile($baseUrl.'/js/flot/jquery.flot.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/flot/jquery.flot.crosshair.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/listenerstat/listenerstat.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $offset = date("Z") * -1; diff --git a/airtime_mvc/application/models/ListenerStat.php b/airtime_mvc/application/models/ListenerStat.php index 8e9a9e4b6..f5567fed5 100644 --- a/airtime_mvc/application/models/ListenerStat.php +++ b/airtime_mvc/application/models/ListenerStat.php @@ -7,25 +7,44 @@ class Application_Model_ListenerStat public static function getDataPointsWithinRange($p_start, $p_end) { $sql = <<=:p1 AND ts.timestamp <= :p2) - ORDER BY mount_name, timestamp +WHERE (ts.timestamp >=:p1 AND ts.timestamp <=:p2) +group by mount_name SQL; $data = Application_Common_Database::prepareAndExecute($sql, - array('p1'=>$p_start, 'p2'=>$p_end)); + array('p1'=>$p_start, 'p2'=>$p_end)); $out = array(); foreach ($data as $d) { - $t = new DateTime($d['timestamp'], new DateTimeZone("UTC")); - $t->setTimezone(new DateTimeZone(date_default_timezone_get())); - // tricking javascript so it thinks the server timezone is in UTC - $dt = new DateTime($t->format("Y-m-d H:i:s"), new DateTimeZone("UTC")); + $jump = intval($d['count']/1000); + $jump = max(1, $jump); + $remainder = $jump == 1?0:1; - $d['timestamp'] = $dt->format("U"); - $out[$d['mount_name']][] = $d; + $sql = <<=:p1 AND ts.timestamp <= :p2) AND mount_name=:p3) as temp +WHERE (temp.rownum%:p4) = :p5; +SQL; + $result = Application_Common_Database::prepareAndExecute($sql, + array('p1'=>$p_start, 'p2'=>$p_end, 'p3'=>$d['mount_name'], 'p4'=>$jump, 'p5'=>$remainder)); + foreach ($result as $r) { + $t = new DateTime($r['timestamp'], new DateTimeZone("UTC")); + $t->setTimezone(new DateTimeZone(date_default_timezone_get())); + // tricking javascript so it thinks the server timezone is in UTC + $dt = new DateTime($t->format("Y-m-d H:i:s"), new DateTimeZone("UTC")); + + $r['timestamp'] = $dt->format("U"); + $out[$r['mount_name']][] = $r; + } } return $out; } diff --git a/airtime_mvc/application/views/scripts/listenerstat/index.phtml b/airtime_mvc/application/views/scripts/listenerstat/index.phtml index 3df9f08ca..9f3568869 100644 --- a/airtime_mvc/application/views/scripts/listenerstat/index.phtml +++ b/airtime_mvc/application/views/scripts/listenerstat/index.phtml @@ -1,5 +1,5 @@
- Timestamp vs Listener Count
+ Listener Count Over Time
date_form; ?> diff --git a/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js b/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js index 02ebefab8..a410acf62 100644 --- a/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js +++ b/airtime_mvc/public/js/airtime/listenerstat/listenerstat.js @@ -35,6 +35,7 @@ function getDataAndPlot(startTimestamp, endTimestamp){ } function plot(datasets){ + var plot; data = null; function plotByChoice(doAll) { @@ -81,9 +82,20 @@ function plot(datasets){ numOfTicks = 10; tickSize = (lastTimestamp.getTime() - firstTimestamp.getTime())/1000/numOfTicks; - $.plot($("#flot_placeholder"), data, { + plot = $.plot($("#flot_placeholder"), data, { yaxis: { min: 0, tickDecimals: 0 }, xaxis: { mode: "time", timeformat:"%y/%m/%0d %H:%M", tickSize: [tickSize, "second"] }, + grid: { + hoverable: true, + backgroundColor: { colors: ["#888888", "#999999"] } + }, + series: { + lines: { + show: true, + fill: 0.3 + }, + points: { show: true } + }, legend: { container: $('#legend'), noColumns: 5, @@ -99,6 +111,45 @@ function plot(datasets){ } }); + function showTooltip(x, y, contents) { + $('
' + contents + '
').css( { + position: 'absolute', + display: 'none', + top: y + 5, + left: x + 5, + border: '1px solid #fdd', + padding: '2px', + 'background-color': '#fee', + opacity: 0.80 + }).appendTo("body").fadeIn(200); + } + + var previousPoint = null; + $("#flot_placeholder").bind("plothover", function (event, pos, item) { + if (item) { + if (previousPoint != item.dataIndex) { + previousPoint = item.dataIndex; + + $("#tooltip").remove(); + var y = item.datapoint[1].toFixed(2); + + showTooltip(item.pageX, item.pageY, + "Listener Count on '"+item.series.label + "': " + Math.floor(y)); + } + } + else { + $("#tooltip").remove(); + previousPoint = null; + } + }); + + $("#placeholder").bind("plotclick", function (event, pos, item) { + if (item) { + $("#clickdata").text("You clicked point " + item.dataIndex + " in " + item.series.label + "."); + plot.highlight(item.series, item.datapoint); + } + }); + $('#legend').find("input").click(function(){setTimeout(plotByChoice,100);}); }