Graphs now have the option to show duration as well as play count.

This commit is contained in:
Tim 2015-08-08 13:34:36 +02:00
parent 94d2d04bf9
commit c28d403186
10 changed files with 773 additions and 126 deletions

View file

@ -10,6 +10,14 @@
<span><i class="fa fa-bar-chart"></i> Graphs</span>
</div>
<div class="button-bar hidden-xs">
<div class="btn-group" data-toggle="buttons" id="yaxis-selection">
<label class="btn btn-dark active">
<input type="radio" name="yaxis-options" id="yaxis-count" value="plays" autocomplete="off" checked> Play Count
</label>
<label class="btn btn-dark">
<input type="radio" name="yaxis-options" id="yaxis-duration" value="duration" autocomplete="off"> Play Duration
</label>
</div>
<div class="btn-group" data-toggle="buttons" id="days-selection">
<label class="btn btn-dark active">
<input type="radio" name="date-options" id="graph-30" value="30" autocomplete="off" checked> 30 days
@ -25,15 +33,15 @@
</div>
<div class='table-card-back'>
<ul class="nav nav-pills" role="tablist">
<li role="presentation" class="active"><a id="watch-count-graphs" href="#tabs-1" aria-controls="tabs-1" role="tab" data-toggle="tab">Play Counts</a></li>
<li role="presentation"><a id="watch-totals-graphs" href="#tabs-2" aria-controls="tabs-2" role="tab" data-toggle="tab">Play Totals</a></li>
<li role="presentation" class="active"><a id="watch-count-graphs" href="#tabs-1" aria-controls="tabs-1" role="tab" data-toggle="tab">Plays by period</a></li>
<li role="presentation"><a id="watch-totals-graphs" href="#tabs-3" aria-controls="tabs-2" role="tab" data-toggle="tab">Play Totals</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tabs-1">
<div class="row">
<div class="col-md-12">
<div class="padded-header">
<h3><i class="fa fa-history"></i> Daily Play Count <small>Last <span class="days">30</span> days</small></h3>
<h3><i class="fa fa-history"></i> Daily <span class="yaxis-text">Play count</span> <small>Last <span class="days">30</span> days</small></h3>
</div>
<div class="graphs-instance">
<div class="watch-chart" id="chart_div_plays_by_day">
@ -46,7 +54,7 @@
<div class="row">
<div class="col-md-6">
<div class="padded-header">
<h3><i class="fa fa-calendar"></i> Plays by day of week <small>Last <span class="days">30</span> days</small></h3>
<h3><i class="fa fa-calendar"></i> <span class="yaxis-text">Play count</span> by day of week <small>Last <span class="days">30</span> days</small></h3>
</div>
<div class="graphs-instance">
<div class="watch-chart" id="chart_div_plays_by_dayofweek" style="float: left;">
@ -58,7 +66,7 @@
</div>
<div class="col-md-6">
<div class="padded-header">
<h3><i class="fa fa-clock-o"></i> Plays by hour of day <small>Last <span class="days">30</span> days</small></h3>
<h3><i class="fa fa-clock-o"></i> <span class="yaxis-text">Play count</span> by hour of day <small>Last <span class="days">30</span> days</small></h3>
</div>
<div class="graphs-instance">
<div class="watch-chart" id="chart_div_plays_by_hourofday">
@ -72,7 +80,7 @@
<div class="row">
<div class="col-md-6">
<div class="padded-header">
<h3><i class="fa fa-television"></i> Plays by top 10 platforms <small>Last <span class="days">30</span> days</small></h3>
<h3><i class="fa fa-television"></i> <span class="yaxis-text">Play count</span> by top 10 platforms <small>Last <span class="days">30</span> days</small></h3>
</div>
<div class="graphs-instance">
<div class="watch-chart" id="chart_div_plays_by_platform" style="float: left;">
@ -84,7 +92,7 @@
</div>
<div class="col-md-6">
<div class="padded-header">
<h3><i class="fa fa-user"></i> Plays by top 10 users <small>Last <span class="days">30</span> days</small></h3>
<h3><i class="fa fa-user"></i> <span class="yaxis-text">Play count</span> by top 10 users <small>Last <span class="days">30</span> days</small></h3>
</div>
<div class="graphs-instance">
<div class="watch-chart" id="chart_div_plays_by_user">
@ -97,7 +105,7 @@
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-2">
<div role="tabpanel" class="tab-pane" id="tabs-3">
<div class="row">
<div class="col-md-12">
<div class="padded-header">
@ -121,6 +129,7 @@
<%def name="javascriptIncludes()">
<script src="interfaces/default/js/moment-with-locale.js"></script>
<script src="interfaces/default/js/moment-duration-format.js"></script>
<script src="interfaces/default/js/highcharts/js/highcharts.js"></script>
<script src="interfaces/default/js/graphs/plays_by_day.js"></script>
<script src="interfaces/default/js/graphs/plays_by_dayofweek.js"></script>
@ -132,13 +141,15 @@
$(document).ready(function () {
var current_range = 30;
var yaxis = 'plays';
function loadGraphsTab1(time_range) {
function loadGraphsTab1(time_range, yaxis) {
setGraphFormat(yaxis);
$.ajax({
url: "get_plays_by_date",
type: 'get',
data: { time_range: time_range },
data: { time_range: time_range, y_axis: yaxis },
dataType: "json",
cache: false,
success: function(data) {
@ -156,7 +167,7 @@
$.ajax({
url: "get_plays_by_dayofweek",
type: 'get',
data: { time_range: time_range },
data: { time_range: time_range, y_axis: yaxis },
dataType: "json",
cache: false,
success: function(data) {
@ -169,7 +180,7 @@
$.ajax({
url: "get_plays_by_hourofday",
type: 'get',
data: { time_range: time_range },
data: { time_range: time_range, y_axis: yaxis },
dataType: "json",
cache: false,
success: function(data) {
@ -182,7 +193,7 @@
$.ajax({
url: "get_plays_by_top_10_platforms",
type: 'get',
data: { time_range: time_range },
data: { time_range: time_range, y_axis: yaxis },
dataType: "json",
cache: false,
success: function(data) {
@ -195,7 +206,7 @@
$.ajax({
url: "get_plays_by_top_10_users",
type: 'get',
data: { time_range: time_range },
data: { time_range: time_range, y_axis: yaxis },
dataType: "json",
cache: false,
success: function(data) {
@ -206,10 +217,13 @@
});
}
function loadGraphsTab2() {
function loadGraphsTab3(yaxis) {
setGraphFormat(yaxis);
$.ajax({
url: "get_plays_per_month",
type: 'get',
data: { y_axis: yaxis },
dataType: "json",
cache: false,
success: function(data) {
@ -226,25 +240,106 @@
}
// Set initial state
loadGraphsTab1(current_range);
loadGraphsTab1(current_range, yaxis);
$('#watch-count-graphs').click(function() {
loadGraphsTab1(current_range);
loadGraphsTab1(current_range, yaxis);
$('#days-selection').show();
});
$('#watch-totals-graphs').click(function() {
loadGraphsTab2();
loadGraphsTab3(yaxis);
$('#days-selection').hide();
});
$('#days-selection').on('change', function() {
current_range = $('input[name=date-options]:checked', '#days-selection').val();
loadGraphsTab1(current_range);
loadGraphsTab1(current_range, yaxis);
$('.days').html(current_range);
});
$('#yaxis-selection').on('change', function() {
yaxis = $('input[name=yaxis-options]:checked', '#yaxis-selection').val();
loadGraphsTab1(current_range, yaxis);
loadGraphsTab3(yaxis);
$('.days').html(current_range);
});
function setGraphFormat(type) {
if (type === 'plays') {
yaxis_format = function() { return this.value; };
hc_plays_by_day_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_dayofweek_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_hourofday_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_platform_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_user_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_month_options.yAxis.labels.formatter = yaxis_format;
tooltip_format = function() {
if (moment(this.x).isValid()) {
var s = '<b>'+ moment(this.x).format("ddd MMM D") +'</b>';
} else {
var s = '<b>'+ this.x +'</b>';
}
$.each(this.points, function(i, point) {
s += '<br/>'+point.series.name+': '+point.y;
});
return s;
}
stack_labels_format = function() {
return this.total;
}
hc_plays_by_day_options.tooltip.formatter = tooltip_format;
hc_plays_by_dayofweek_options.tooltip.formatter = tooltip_format;
hc_plays_by_hourofday_options.tooltip.formatter = tooltip_format;
hc_plays_by_platform_options.tooltip.formatter = tooltip_format;
hc_plays_by_user_options.tooltip.formatter = tooltip_format;
hc_plays_by_month_options.tooltip.formatter = tooltip_format;
hc_plays_by_month_options.yAxis.stackLabels.formatter = stack_labels_format;
$('.yaxis-text').html('Play count');
} else {
yaxis_format = function() { return moment.duration(this.value, 'seconds').format("m [mins]"); };
hc_plays_by_day_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_dayofweek_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_hourofday_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_platform_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_user_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_month_options.yAxis.labels.formatter = yaxis_format;
tooltip_format = function() {
if (moment(this.x).isValid()) {
var s = '<b>'+ moment(this.x).format("ddd MMM D") +'</b>';
} else {
var s = '<b>'+ this.x +'</b>';
}
$.each(this.points, function(i, point) {
s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'seconds').format('D [days] H [hrs] m [mins]');
});
return s;
}
stack_labels_format = function() {
if (moment(this.total).isValid()) {
console.log(this);
var s = moment.duration(this.total, 'seconds').format("H [hrs] m [mins]");
} else {
var s = this.total;
}
return s;
}
hc_plays_by_day_options.tooltip.formatter = tooltip_format;
hc_plays_by_dayofweek_options.tooltip.formatter = tooltip_format;
hc_plays_by_hourofday_options.tooltip.formatter = tooltip_format;
hc_plays_by_platform_options.tooltip.formatter = tooltip_format;
hc_plays_by_user_options.tooltip.formatter = tooltip_format;
hc_plays_by_month_options.tooltip.formatter = tooltip_format;
hc_plays_by_month_options.yAxis.stackLabels.formatter = stack_labels_format;
$('.yaxis-text').html('Play duration');
}
}
});
</script>
</%def>

View file

@ -47,15 +47,6 @@ var hc_plays_by_day_options = {
}
},
tooltip: {
formatter: function() {
var monthStr = moment(this.x).format("ddd MMM D");
var s = '<b>'+ monthStr +'</b>';
$.each(this.points, function(i, point) {
s += '<br/>'+point.series.name+': '+point.y;
});
return s;
},
shared: true
},
series: [{}]

View file

@ -49,7 +49,7 @@ var hc_plays_by_dayofweek_options = {
}
},
tooltip: {
shared: true
},
series: [{}]
};

View file

@ -49,7 +49,7 @@ var hc_plays_by_hourofday_options = {
}
},
tooltip: {
shared: true
},
series: [{}]
};

View file

@ -65,15 +65,6 @@ var hc_plays_by_month_options = {
}
},
tooltip: {
formatter: function() {
var monthStr = moment(this.x).format("MMM YYYY");
var s = '<b>'+ monthStr +'</b>';
$.each(this.points, function(i, point) {
s += '<br/>'+point.series.name+': '+point.y;
});
return s;
},
shared: true
},
series: [{}]

View file

@ -49,7 +49,7 @@ var hc_plays_by_platform_options = {
}
},
tooltip: {
shared: true
},
series: [{}]
};

View file

@ -49,7 +49,7 @@ var hc_plays_by_user_options = {
}
},
tooltip: {
shared: true
},
series: [{}]
};

View file

@ -0,0 +1,482 @@
/*! Moment Duration Format v1.3.0
* https://github.com/jsmreese/moment-duration-format
* Date: 2014-07-15
*
* Duration format plugin function for the Moment.js library
* http://momentjs.com/
*
* Copyright 2014 John Madhavan-Reese
* Released under the MIT license
*/
(function (root, undefined) {
// repeatZero(qty)
// returns "0" repeated qty times
function repeatZero(qty) {
var result = "";
// exit early
// if qty is 0 or a negative number
// or doesn't coerce to an integer
qty = parseInt(qty, 10);
if (!qty || qty < 1) { return result; }
while (qty) {
result += "0";
qty -= 1;
}
return result;
}
// padZero(str, len [, isRight])
// pads a string with zeros up to a specified length
// will not pad a string if its length is aready
// greater than or equal to the specified length
// default output pads with zeros on the left
// set isRight to `true` to pad with zeros on the right
function padZero(str, len, isRight) {
if (str == null) { str = ""; }
str = "" + str;
return (isRight ? str : "") + repeatZero(len - str.length) + (isRight ? "" : str);
}
// isArray
function isArray(array) {
return Object.prototype.toString.call(array) === "[object Array]";
}
// isObject
function isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
// findLast
function findLast(array, callback) {
var index = array.length;
while (index -= 1) {
if (callback(array[index])) { return array[index]; }
}
}
// find
function find(array, callback) {
var index = 0,
max = array.length,
match;
if (typeof callback !== "function") {
match = callback;
callback = function (item) {
return item === match;
};
}
while (index < max) {
if (callback(array[index])) { return array[index]; }
index += 1;
}
}
// each
function each(array, callback) {
var index = 0,
max = array.length;
if (!array || !max) { return; }
while (index < max) {
if (callback(array[index], index) === false) { return; }
index += 1;
}
}
// map
function map(array, callback) {
var index = 0,
max = array.length,
ret = [];
if (!array || !max) { return ret; }
while (index < max) {
ret[index] = callback(array[index], index);
index += 1;
}
return ret;
}
// pluck
function pluck(array, prop) {
return map(array, function (item) {
return item[prop];
});
}
// compact
function compact(array) {
var ret = [];
each(array, function (item) {
if (item) { ret.push(item); }
});
return ret;
}
// unique
function unique(array) {
var ret = [];
each(array, function (_a) {
if (!find(ret, _a)) { ret.push(_a); }
});
return ret;
}
// intersection
function intersection(a, b) {
var ret = [];
each(a, function (_a) {
each(b, function (_b) {
if (_a === _b) { ret.push(_a); }
});
});
return unique(ret);
}
// rest
function rest(array, callback) {
var ret = [];
each(array, function (item, index) {
if (!callback(item)) {
ret = array.slice(index);
return false;
}
});
return ret;
}
// initial
function initial(array, callback) {
var reversed = array.slice().reverse();
return rest(reversed, callback).reverse();
}
// extend
function extend(a, b) {
for (var key in b) {
if (b.hasOwnProperty(key)) { a[key] = b[key]; }
}
return a;
}
// define internal moment reference
var moment;
if (typeof require === "function") {
try { moment = require('moment'); }
catch (e) {}
}
if (!moment && root.moment) {
moment = root.moment;
}
if (!moment) {
throw "Moment Duration Format cannot find Moment.js";
}
// moment.duration.format([template] [, precision] [, settings])
moment.duration.fn.format = function () {
var tokenizer, tokens, types, typeMap, momentTypes, foundFirst, trimIndex,
args = [].slice.call(arguments),
settings = extend({}, this.format.defaults),
// keep a shadow copy of this moment for calculating remainders
remainder = moment.duration(this);
// add a reference to this duration object to the settings for use
// in a template function
settings.duration = this;
// parse arguments
each(args, function (arg) {
if (typeof arg === "string" || typeof arg === "function") {
settings.template = arg;
return;
}
if (typeof arg === "number") {
settings.precision = arg;
return;
}
if (isObject(arg)) {
extend(settings, arg);
}
});
// types
types = settings.types = (isArray(settings.types) ? settings.types : settings.types.split(" "));
// template
if (typeof settings.template === "function") {
settings.template = settings.template.apply(settings);
}
// tokenizer regexp
tokenizer = new RegExp(map(types, function (type) {
return settings[type].source;
}).join("|"), "g");
// token type map function
typeMap = function (token) {
return find(types, function (type) {
return settings[type].test(token);
});
};
// tokens array
tokens = map(settings.template.match(tokenizer), function (token, index) {
var type = typeMap(token),
length = token.length;
return {
index: index,
length: length,
// replace escaped tokens with the non-escaped token text
token: (type === "escape" ? token.replace(settings.escape, "$1") : token),
// ignore type on non-moment tokens
type: ((type === "escape" || type === "general") ? null : type)
// calculate base value for all moment tokens
//baseValue: ((type === "escape" || type === "general") ? null : this.as(type))
};
}, this);
// unique moment token types in the template (in order of descending magnitude)
momentTypes = intersection(types, unique(compact(pluck(tokens, "type"))));
// exit early if there are no momentTypes
if (!momentTypes.length) {
return pluck(tokens, "token").join("");
}
// calculate values for each token type in the template
each(momentTypes, function (momentType, index) {
var value, wholeValue, decimalValue, isLeast, isMost;
// calculate integer and decimal value portions
value = remainder.as(momentType);
wholeValue = (value > 0 ? Math.floor(value) : Math.ceil(value));
decimalValue = value - wholeValue;
// is this the least-significant moment token found?
isLeast = ((index + 1) === momentTypes.length);
// is this the most-significant moment token found?
isMost = (!index);
// update tokens array
// using this algorithm to not assume anything about
// the order or frequency of any tokens
each(tokens, function (token) {
if (token.type === momentType) {
extend(token, {
value: value,
wholeValue: wholeValue,
decimalValue: decimalValue,
isLeast: isLeast,
isMost: isMost
});
if (isMost) {
// note the length of the most-significant moment token:
// if it is greater than one and forceLength is not set, default forceLength to `true`
if (settings.forceLength == null && token.length > 1) {
settings.forceLength = true;
}
// rationale is this:
// if the template is "h:mm:ss" and the moment value is 5 minutes, the user-friendly output is "5:00", not "05:00"
// shouldn't pad the `minutes` token even though it has length of two
// if the template is "hh:mm:ss", the user clearly wanted everything padded so we should output "05:00"
// if the user wanted the full padded output, they can set `{ trim: false }` to get "00:05:00"
}
}
});
// update remainder
remainder.subtract(wholeValue, momentType);
});
// trim tokens array
if (settings.trim) {
tokens = (settings.trim === "left" ? rest : initial)(tokens, function (token) {
// return `true` if:
// the token is not the least moment token (don't trim the least moment token)
// the token is a moment token that does not have a value (don't trim moment tokens that have a whole value)
return !(token.isLeast || (token.type != null && token.wholeValue));
});
}
// build output
// the first moment token can have special handling
foundFirst = false;
// run the map in reverse order if trimming from the right
if (settings.trim === "right") {
tokens.reverse();
}
tokens = map(tokens, function (token) {
var val,
decVal;
if (!token.type) {
// if it is not a moment token, use the token as its own value
return token.token;
}
// apply negative precision formatting to the least-significant moment token
if (token.isLeast && (settings.precision < 0)) {
val = (Math.floor(token.wholeValue * Math.pow(10, settings.precision)) * Math.pow(10, -settings.precision)).toString();
} else {
val = token.wholeValue.toString();
}
// remove negative sign from the beginning
val = val.replace(/^\-/, "");
// apply token length formatting
// special handling for the first moment token that is not the most significant in a trimmed template
if (token.length > 1 && (foundFirst || token.isMost || settings.forceLength)) {
val = padZero(val, token.length);
}
// add decimal value if precision > 0
if (token.isLeast && (settings.precision > 0)) {
decVal = token.decimalValue.toString().replace(/^\-/, "").split(/\.|e\-/);
switch (decVal.length) {
case 1:
val += "." + padZero(decVal[0], settings.precision, true).slice(0, settings.precision);
break;
case 2:
val += "." + padZero(decVal[1], settings.precision, true).slice(0, settings.precision);
break;
case 3:
val += "." + padZero(repeatZero((+decVal[2]) - 1) + (decVal[0] || "0") + decVal[1], settings.precision, true).slice(0, settings.precision);
break;
default:
throw "Moment Duration Format: unable to parse token decimal value.";
}
}
// add a negative sign if the value is negative and token is most significant
if (token.isMost && token.value < 0) {
val = "-" + val;
}
foundFirst = true;
return val;
});
// undo the reverse if trimming from the right
if (settings.trim === "right") {
tokens.reverse();
}
return tokens.join("");
};
moment.duration.fn.format.defaults = {
// token definitions
escape: /\[(.+?)\]/,
years: /[Yy]+/,
months: /M+/,
weeks: /[Ww]+/,
days: /[Dd]+/,
hours: /[Hh]+/,
minutes: /m+/,
seconds: /s+/,
milliseconds: /S+/,
general: /.+?/,
// token type names
// in order of descending magnitude
// can be a space-separated token name list or an array of token names
types: "escape years months weeks days hours minutes seconds milliseconds general",
// format options
// trim
// "left" - template tokens are trimmed from the left until the first moment token that has a value >= 1
// "right" - template tokens are trimmed from the right until the first moment token that has a value >= 1
// (the final moment token is not trimmed, regardless of value)
// `false` - template tokens are not trimmed
trim: "left",
// precision
// number of decimal digits to include after (to the right of) the decimal point (positive integer)
// or the number of digits to truncate to 0 before (to the left of) the decimal point (negative integer)
precision: 0,
// force first moment token with a value to render at full length even when template is trimmed and first moment token has length of 1
forceLength: null,
// template used to format duration
// may be a function or a string
// template functions are executed with the `this` binding of the settings object
// so that template strings may be dynamically generated based on the duration object
// (accessible via `this.duration`)
// or any of the other settings
template: function () {
var types = this.types,
dur = this.duration,
lastType = findLast(types, function (type) {
return dur._data[type];
});
// default template strings for each duration dimension type
switch (lastType) {
case "seconds":
return "h:mm:ss";
case "minutes":
return "d[d] h:mm";
case "hours":
return "d[d] h[h]";
case "days":
return "M[m] d[d]";
case "weeks":
return "y[y] w[w]";
case "months":
return "y[y] M[m]";
case "years":
return "y[y]";
default:
return "y[y] M[m] d[d] h:mm:ss";
}
}
};
})(this);

View file

@ -23,22 +23,33 @@ class Graphs(object):
def __init__(self):
pass
def get_total_plays_per_day(self, time_range='30'):
def get_total_plays_per_day(self, time_range='30', y_axis='plays'):
monitor_db = database.MonitorDatabase()
if not time_range.isdigit():
time_range = '30'
try:
query = 'SELECT date(started, "unixepoch", "localtime") as date_played, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY date_played ' \
'ORDER BY started ASC' % time_range
if y_axis == 'plays':
query = 'SELECT date(started, "unixepoch", "localtime") as date_played, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY date_played ' \
'ORDER BY started ASC' % time_range
result = monitor_db.select(query)
result = monitor_db.select(query)
else:
query = 'SELECT date(started, "unixepoch", "localtime") as date_played, ' \
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) else 0 end) as tv_duration, ' \
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) else 0 end) as movie_duration ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY date_played ' \
'ORDER BY started ASC' % time_range
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query.")
return None
@ -78,29 +89,50 @@ class Graphs(object):
'series': [series_1_output, series_2_output]}
return output
def get_total_plays_per_dayofweek(self, time_range='30'):
def get_total_plays_per_dayofweek(self, time_range='30', y_axis='plays'):
monitor_db = database.MonitorDatabase()
if not time_range.isdigit():
time_range = '30'
query = 'SELECT strftime("%w", datetime(started, "unixepoch", "localtime")) as daynumber, ' \
'case cast (strftime("%w", datetime(started, "unixepoch", "localtime")) as integer) ' \
'when 0 then "Sunday" ' \
'when 1 then "Monday" ' \
'when 2 then "Tuesday" ' \
'when 3 then "Wednesday" ' \
'when 4 then "Thursday" ' \
'when 5 then "Friday" ' \
'else "Saturday" end as dayofweek, ' \
'COUNT(id) as total_plays ' \
'from session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'GROUP BY dayofweek ' \
'ORDER BY daynumber'
if y_axis == 'plays':
query = 'SELECT strftime("%w", datetime(started, "unixepoch", "localtime")) as daynumber, ' \
'case cast (strftime("%w", datetime(started, "unixepoch", "localtime")) as integer) ' \
'when 0 then "Sunday" ' \
'when 1 then "Monday" ' \
'when 2 then "Tuesday" ' \
'when 3 then "Wednesday" ' \
'when 4 then "Thursday" ' \
'when 5 then "Friday" ' \
'else "Saturday" end as dayofweek, ' \
'COUNT(id) as total_plays ' \
'from session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'GROUP BY dayofweek ' \
'ORDER BY daynumber'
result = monitor_db.select(query)
result = monitor_db.select(query)
y_axis_label = 'Total plays'
else:
query = 'SELECT strftime("%w", datetime(started, "unixepoch", "localtime")) as daynumber, ' \
'case cast (strftime("%w", datetime(started, "unixepoch", "localtime")) as integer) ' \
'when 0 then "Sunday" ' \
'when 1 then "Monday" ' \
'when 2 then "Tuesday" ' \
'when 3 then "Wednesday" ' \
'when 4 then "Thursday" ' \
'when 5 then "Friday" ' \
'else "Saturday" end as dayofweek, ' \
'SUM(case when media_type != "track" and stopped > 0 then (stopped - started) else 0 end) as duration ' \
'from session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'GROUP BY dayofweek ' \
'ORDER BY daynumber'
result = monitor_db.select(query)
y_axis_label = 'Total duration'
days_list = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday']
@ -120,28 +152,41 @@ class Graphs(object):
series_1.append(series_1_value)
series_1_output = {'name': 'Total plays',
series_1_output = {'name': y_axis_label,
'data': series_1}
output = {'categories': categories,
'series': [series_1_output]}
return output
def get_total_plays_per_hourofday(self, time_range='30'):
def get_total_plays_per_hourofday(self, time_range='30', y_axis='plays'):
monitor_db = database.MonitorDatabase()
if not time_range.isdigit():
time_range = '30'
query = 'select strftime("%H", datetime(started, "unixepoch", "localtime")) as hourofday, ' \
'COUNT(id) ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'GROUP BY hourofday ' \
'ORDER BY hourofday'
if y_axis == 'plays':
query = 'select strftime("%H", datetime(started, "unixepoch", "localtime")) as hourofday, ' \
'COUNT(id) ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'GROUP BY hourofday ' \
'ORDER BY hourofday'
result = monitor_db.select(query)
result = monitor_db.select(query)
y_axis_label = 'Total plays'
else:
query = 'select strftime("%H", datetime(started, "unixepoch", "localtime")) as hourofday, ' \
'SUM(case when media_type != "track" and stopped > 0 then (stopped - started) else 0 end) as duration ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'GROUP BY hourofday ' \
'ORDER BY hourofday'
result = monitor_db.select(query)
y_axis_label = 'Total duration'
hours_list = ['00','01','02','03','04','05',
'06','07','08','09','10','11',
@ -163,27 +208,37 @@ class Graphs(object):
series_1.append(series_1_value)
series_1_output = {'name': 'Total plays',
series_1_output = {'name': y_axis_label,
'data': series_1}
output = {'categories': categories,
'series': [series_1_output]}
return output
def get_total_plays_per_month(self):
def get_total_plays_per_month(self, y_axis='plays'):
import time as time
monitor_db = database.MonitorDatabase()
if y_axis == 'plays':
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) as datestring, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count ' \
'FROM session_history ' \
'WHERE datetime(started, "unixepoch", "localtime") >= datetime("now", "-12 months", "localtime") ' \
'GROUP BY strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) ' \
'ORDER BY datestring DESC LIMIT 12'
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) as datestring, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count ' \
'FROM session_history ' \
'WHERE datetime(started, "unixepoch", "localtime") >= datetime("now", "-12 months", "localtime") ' \
'GROUP BY strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) ' \
'ORDER BY datestring DESC LIMIT 12'
result = monitor_db.select(query)
else:
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) as datestring, ' \
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) else 0 end) as tv_duration, ' \
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) else 0 end) as movie_duration ' \
'FROM session_history ' \
'WHERE datetime(started, "unixepoch", "localtime") >= datetime("now", "-12 months", "localtime") ' \
'GROUP BY strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) ' \
'ORDER BY datestring DESC LIMIT 12'
result = monitor_db.select(query)
result = monitor_db.select(query)
# create our date range as some months may not have any data
# but we still want to display them
@ -224,23 +279,38 @@ class Graphs(object):
'series': [series_1_output, series_2_output]}
return output
def get_total_plays_by_top_10_platforms(self, time_range='30'):
def get_total_plays_by_top_10_platforms(self, time_range='30', y_axis='plays'):
monitor_db = database.MonitorDatabase()
if not time_range.isdigit():
time_range = '30'
query = 'SELECT platform, ' \
'count(id) as platform_count ' \
'FROM session_history ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'(media_type = "episode" OR media_type = "movie") ' \
'GROUP BY platform ' \
'ORDER BY platform_count DESC ' \
'LIMIT 10'
if y_axis == 'plays':
query = 'SELECT platform, ' \
'count(id) as platform_count ' \
'FROM session_history ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'(media_type = "episode" OR media_type = "movie") ' \
'GROUP BY platform ' \
'ORDER BY platform_count DESC ' \
'LIMIT 10'
result = monitor_db.select(query)
result = monitor_db.select(query)
y_axis_label = 'Total plays'
else:
query = 'SELECT platform, ' \
'SUM(case when stopped > 0 then (stopped - started) else 0 end) as duration ' \
'FROM session_history ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'(media_type = "episode" OR media_type = "movie") ' \
'GROUP BY platform ' \
'ORDER BY duration DESC ' \
'LIMIT 10'
result = monitor_db.select(query)
y_axis_label = 'Total duration'
categories = []
series_1 = []
@ -249,33 +319,51 @@ class Graphs(object):
categories.append(item[0])
series_1.append(item[1])
series_1_output = {'name': 'Total plays',
series_1_output = {'name': y_axis_label,
'data': series_1}
output = {'categories': categories,
'series': [series_1_output]}
return output
def get_total_plays_by_top_10_users(self, time_range='30'):
def get_total_plays_by_top_10_users(self, time_range='30', y_axis='plays'):
monitor_db = database.MonitorDatabase()
if not time_range.isdigit():
time_range = '30'
query = 'SELECT ' \
'(case when users.friendly_name is null then session_history.user else ' \
'users.friendly_name end) as friendly_name,' \
'count(session_history.id) as user_count ' \
'FROM session_history ' \
'JOIN users on session_history.user_id = users.user_id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
'GROUP BY session_history.user_id ' \
'ORDER BY user_count DESC ' \
'LIMIT 10'
if y_axis == 'plays':
query = 'SELECT ' \
'(case when users.friendly_name is null then session_history.user else ' \
'users.friendly_name end) as friendly_name,' \
'count(session_history.id) as user_count ' \
'FROM session_history ' \
'JOIN users on session_history.user_id = users.user_id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
'GROUP BY session_history.user_id ' \
'ORDER BY user_count DESC ' \
'LIMIT 10'
result = monitor_db.select(query)
result = monitor_db.select(query)
y_axis_label = 'Total plays'
else:
query = 'SELECT ' \
'(case when users.friendly_name is null then session_history.user else ' \
'users.friendly_name end) as friendly_name,' \
'SUM(case when stopped > 0 then (stopped - started) else 0 end) as duration ' \
'FROM session_history ' \
'JOIN users on session_history.user_id = users.user_id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
'GROUP BY session_history.user_id ' \
'ORDER BY duration DESC ' \
'LIMIT 10'
result = monitor_db.select(query)
y_axis_label = 'Total duration'
categories = []
series_1 = []
@ -284,7 +372,7 @@ class Graphs(object):
categories.append(item[0])
series_1.append(item[1])
series_1_output = {'name': 'Total plays',
series_1_output = {'name': y_axis_label,
'data': series_1}
output = {'categories': categories,

View file

@ -823,10 +823,10 @@ class WebInterface(object):
return json.dumps(history)
@cherrypy.expose
def get_plays_by_date(self, time_range='30', **kwargs):
def get_plays_by_date(self, time_range='30', y_axis='plays', **kwargs):
graph = graphs.Graphs()
result = graph.get_total_plays_per_day(time_range=time_range)
result = graph.get_total_plays_per_day(time_range=time_range, y_axis=y_axis)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
@ -835,10 +835,10 @@ class WebInterface(object):
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_plays_by_dayofweek(self, time_range='30', **kwargs):
def get_plays_by_dayofweek(self, time_range='30', y_axis='plays', **kwargs):
graph = graphs.Graphs()
result = graph.get_total_plays_per_dayofweek(time_range=time_range)
result = graph.get_total_plays_per_dayofweek(time_range=time_range, y_axis=y_axis)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
@ -847,10 +847,10 @@ class WebInterface(object):
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_plays_by_hourofday(self, time_range='30', **kwargs):
def get_plays_by_hourofday(self, time_range='30', y_axis='plays', **kwargs):
graph = graphs.Graphs()
result = graph.get_total_plays_per_hourofday(time_range=time_range)
result = graph.get_total_plays_per_hourofday(time_range=time_range, y_axis=y_axis)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
@ -859,10 +859,10 @@ class WebInterface(object):
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_plays_per_month(self, **kwargs):
def get_plays_per_month(self, y_axis='plays', **kwargs):
graph = graphs.Graphs()
result = graph.get_total_plays_per_month()
result = graph.get_total_plays_per_month(y_axis=y_axis)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
@ -871,10 +871,10 @@ class WebInterface(object):
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_plays_by_top_10_platforms(self, time_range='30', **kwargs):
def get_plays_by_top_10_platforms(self, time_range='30', y_axis='plays', **kwargs):
graph = graphs.Graphs()
result = graph.get_total_plays_by_top_10_platforms(time_range=time_range)
result = graph.get_total_plays_by_top_10_platforms(time_range=time_range, y_axis=y_axis)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
@ -883,10 +883,10 @@ class WebInterface(object):
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_plays_by_top_10_users(self, time_range='30', **kwargs):
def get_plays_by_top_10_users(self, time_range='30', y_axis='plays', **kwargs):
graph = graphs.Graphs()
result = graph.get_total_plays_by_top_10_users(time_range=time_range)
result = graph.get_total_plays_by_top_10_users(time_range=time_range, y_axis=y_axis)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'