Add platform stats to user page

Re-add user gravatars
Some error catching
Some code clean-up
This commit is contained in:
Tim 2015-06-21 17:19:19 +02:00
parent ba18c5b96e
commit 9364b06c99
12 changed files with 275 additions and 76 deletions

View file

@ -51,7 +51,7 @@ A python based web front-end for plexWatch.
* full user list with general information and comparison stats * full user list with general information and comparison stats
* individual user information **PARTIALLY IMPLEMENTED** * individual user information
- username and gravatar (if available) - username and gravatar (if available)
- daily, weekly, monthly, all time stats for play count and duration length - daily, weekly, monthly, all time stats for play count and duration length
- individual platform stats for each user - individual platform stats for each user

View file

@ -7635,6 +7635,7 @@ button.close {
} }
.user-info-poster-face img { .user-info-poster-face img {
bottom: 0; bottom: 0;
margin-right: 15px;
overflow: hidden; overflow: hidden;
float: left; float: left;
background-color: #323232; background-color: #323232;
@ -7651,12 +7652,11 @@ button.close {
color: #fff; color: #fff;
position: relative; position: relative;
top: 27px; top: 27px;
left: 15px;
} }
.user-info-nav { .user-info-nav {
position: relative; position: relative;
top: 15px; top: 15px;
left: 3px; left: -5px;
} }
.user-info-nav > .active > a, .nav-tabs > .active > a:hover, .nav-tabs > .active > a:focus { .user-info-nav > .active > a, .nav-tabs > .active > a:hover, .nav-tabs > .active > a:focus {
color: #F9AA03; color: #F9AA03;

View file

@ -210,13 +210,17 @@ function getPlatformImagePath(platformName) {
} }
function isPrivateIP(ip_address) { function isPrivateIP(ip_address) {
var parts = ip_address.split('.'); if (ip_address.indexOf(".") > -1) {
if (parts[0] === '10' || var parts = ip_address.split('.');
(parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) || if (parts[0] === '10' ||
(parts[0] === '192' && parts[1] === '168')) { (parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) ||
(parts[0] === '192' && parts[1] === '168')) {
return true;
}
return false;
} else {
return true; return true;
} }
return false;
} }
function humanTime(seconds) { function humanTime(seconds) {

View file

@ -32,7 +32,11 @@ user_ip_table_options = {
"className": "modal-control", "className": "modal-control",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (isPrivateIP(cellData)) { if (isPrivateIP(cellData)) {
$(td).html(cellData); if (cellData != '') {
$(td).html(cellData);
} else {
$(td).html('n/a');
}
} else { } else {
$(td).html('<a href="#ip-info-modal" data-toggle="modal"><span data-toggle="ip-tooltip" data-placement="left" title="IP Address Info" id="ip-info"><i class="icon-map-marker icon-white"></i></span>&nbsp' + cellData +'</a>'); $(td).html('<a href="#ip-info-modal" data-toggle="modal"><span data-toggle="ip-tooltip" data-placement="left" title="IP Address Info" id="ip-info"><i class="icon-map-marker icon-white"></i></span>&nbsp' + cellData +'</a>');
} }

View file

@ -25,13 +25,13 @@ users_list_table_options = {
"columnDefs": [ "columnDefs": [
{ {
"targets": [0], "targets": [0],
"data": null, "data": "thumb",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
//if (rowData['user_thumb'] === '') { if (cellData === '') {
$(td).html('<img src="interfaces/default/images/gravatar-default-80x80.png" alt="User Logo"/>'); $(td).html('<img src="interfaces/default/images/gravatar-default-80x80.png" alt="User Logo"/>');
//} else { } else {
// $(td).html('<img src="' + rowData['user_thumb'] + '" alt="User Logo"/>'); $(td).html('<img src="' + cellData + '" alt="User Logo"/>');
//} }
}, },
"orderable": false, "orderable": false,
"className": "users-poster-face", "className": "users-poster-face",

View file

@ -13,7 +13,7 @@
<div class="row-fluid"> <div class="row-fluid">
<div class="span12"> <div class="span12">
<div class="user-info-wrapper"> <div class="user-info-wrapper">
<div class="user-info-poster-face"> <div class="user-info-poster-face" id="user-gravatar">
<img src="interfaces/default/images/gravatar-default-80x80.png"> <img src="interfaces/default/images/gravatar-default-80x80.png">
</div> </div>
<div class="user-info-username"> <div class="user-info-username">
@ -42,7 +42,7 @@
</div> </div>
</div> </div>
<div id="user-time-stats" class="user-overview-stats-wrapper"> <div id="user-time-stats" class="user-overview-stats-wrapper">
<div id="user-stats-spinner" class="spinner"></div> <div class='muted'><i class="fa fa-refresh fa-spin"></i> Loading data...</div><br>
</div> </div>
</div> </div>
</div> </div>
@ -58,7 +58,7 @@
</div> </div>
</div> </div>
<div id="user-platform-stats" class="user-platforms"> <div id="user-platform-stats" class="user-platforms">
<div id="user-platform-spinner" class="spinner"></div> <div class='muted'><i class="fa fa-refresh fa-spin"></i> Loading data...</div><br>
</div> </div>
</div> </div>
</div> </div>
@ -73,8 +73,8 @@
<h3>Recently watched</h3> <h3>Recently watched</h3>
</div> </div>
</div> </div>
<div id="user-recently-watched" class="dashboard-recent-media-row"> <div id="user-recently-watched">
<div id="user-watched-spinner" class="spinner"></div> <div class='muted'><i class="fa fa-refresh fa-spin"></i> Loading data...</div><br>
</div> </div>
</div> </div>
</div> </div>
@ -88,7 +88,7 @@
<div class="wellbg"> <div class="wellbg">
<div class="wellheader"> <div class="wellheader">
<div class="dashboard-wellheader"> <div class="dashboard-wellheader">
<h3>Public IP Addresses for <strong> <h3>IP Addresses for <strong>
${user} ${user}
</strong></h3> </strong></h3>
</div> </div>
@ -190,6 +190,7 @@
<script> <script>
$(document).ready(function () { $(document).ready(function () {
// Populate watch time stats
$.ajax({ $.ajax({
url: 'get_user_watch_time_stats', url: 'get_user_watch_time_stats',
async: true, async: true,
@ -199,6 +200,17 @@
} }
}); });
// Populate platform stats
$.ajax({
url: 'get_user_platform_stats',
async: true,
data: { user: '${user}' },
complete: function(xhr, status) {
$("#user-platform-stats").html(xhr.responseText);
}
});
// Populate recently watched
$.ajax({ $.ajax({
url: 'get_user_recently_watched', url: 'get_user_recently_watched',
async: true, async: true,
@ -208,6 +220,7 @@
} }
}); });
// Build watch history table
history_table_options.ajax = { history_table_options.ajax = {
"url": "get_history", "url": "get_history",
"data": function(d) { "data": function(d) {
@ -217,6 +230,7 @@
history_table = $('#history_table').DataTable(history_table_options); history_table = $('#history_table').DataTable(history_table_options);
history_table.column(2).visible(false); // Hide the title column history_table.column(2).visible(false); // Hide the title column
// Build user IP table
user_ip_table_options.ajax = { user_ip_table_options.ajax = {
"url": "get_user_ips", "url": "get_user_ips",
"data": function(d) { "data": function(d) {
@ -225,6 +239,18 @@
} }
user_ip_table = $('#user_ip_table').DataTable(user_ip_table_options); user_ip_table = $('#user_ip_table').DataTable(user_ip_table_options);
// Load user gravatar image
$.ajax({
url: 'get_user_gravatar_image',
async: true,
data: { user: '${user}' },
success: function(data) {
if (data.user_thumb !== '') {
thumb = data.user_thumb;
$('#user-gravatar').html('<img src="' + thumb + '">');
}
}
});
}); });
</script> </script>
</%def> </%def>

View file

@ -0,0 +1,22 @@
% if platform_stats != None:
% for a in platform_stats:
<ul>
<div class="user-platforms-instance">
<li>
<span id="user-platform-image-${a['result_id']}"></span>
<div class="user-platforms-instance-name">
${a['platform_name']}
</div>
<div class="user-platforms-instance-playcount">
<h3>${a['total_plays']}</h3><p> plays</p>
</div>
</li>
</div>
</ul>
<script>
$("#user-platform-image-${a['result_id']}").html("<img class='user-platforms-instance-poster' src='" + getPlatformImagePath('${a['platform_type']}') + "'>");
</script>
% endfor
% else:
<div class="muted">There was an error loading your PlexWatch data. Please check your <a href="config">settings</a>.</div><br>
% endif

View file

@ -32,5 +32,5 @@
</ul> </ul>
</div> </div>
% else: % else:
<div class="muted">There was an error retrieving some data. Please check your <a href="config">settings</a>.</div><br> <div class="muted">There was an error loading your PlexWatch data. Please check your <a href="config">settings</a>.</div><br>
% endif % endif

View file

@ -9,7 +9,7 @@
% elif a['query_days'] == 1: % elif a['query_days'] == 1:
<h4>Last 24 hours</h4> <h4>Last 24 hours</h4>
% else: % else:
<h4>Last ${a['query_days']} day(s)</h4> <h4>Last ${a['query_days']} days</h4>
% endif % endif
<h3>${a['total_plays']}</h3><p>plays</p> <h3>${a['total_plays']}</h3><p>plays</p>
<span id="total-time-${a['query_days']}"></span> <span id="total-time-${a['query_days']}"></span>
@ -21,4 +21,6 @@
</script> </script>
% endfor % endfor
</ul> </ul>
% else:
<div class="muted">There was an error loading your PlexWatch data. Please check your <a href="config">settings</a>.</div><br>
% endif % endif

View file

@ -76,8 +76,8 @@ class DataTables(object):
order, custom_where) order, custom_where)
# logger.debug(u"Query string: %s" % query) # logger.debug(u"Query string: %s" % query)
filtered = self.ssp_db.select(query) filtered = self.ssp_db.select(query)
if search_value == '': if search_value == '':
totalcount = len(filtered) totalcount = len(filtered)
else: else:

View file

@ -72,27 +72,35 @@ class PlexWatch(object):
'time', 'time',
'ip_address', 'ip_address',
'COUNT(title) as plays'] 'COUNT(title) as plays']
try:
query = data_tables.ssp_query(table_name=self.get_user_table_name(), query = data_tables.ssp_query(table_name=self.get_user_table_name(),
columns=columns, columns=columns,
start=start, start=start,
length=length, length=length,
order_column=int(order_column), order_column=int(order_column),
order_dir=order_dir, order_dir=order_dir,
search_value=search_value, search_value=search_value,
search_regex=search_regex, search_regex=search_regex,
custom_where='', custom_where='',
group_by='user', group_by='user',
kwargs=kwargs) kwargs=kwargs)
except:
logger.warn("Unable to open PlexWatch database.")
return {'recordsFiltered': 0,
'recordsTotal': 0,
'data': 'null'},
users = query['result'] users = query['result']
rows = [] rows = []
for item in users: for item in users:
thumb = self.get_user_gravatar_image(item['user'])
row = {"plays": item['plays'], row = {"plays": item['plays'],
"time": item['time'], "time": item['time'],
"user": item["user"], "user": item["user"],
"ip_address": item["ip_address"] "ip_address": item["ip_address"],
"thumb": thumb['user_thumb']
} }
rows.append(row) rows.append(row)
@ -136,17 +144,23 @@ class PlexWatch(object):
'orig_title as last_watched' 'orig_title as last_watched'
] ]
query = data_tables.ssp_query(table_name=self.get_user_table_name(), try:
columns=columns, query = data_tables.ssp_query(table_name=self.get_user_table_name(),
start=start, columns=columns,
length=length, start=start,
order_column=int(order_column), length=length,
order_dir=order_dir, order_column=int(order_column),
search_value=search_value, order_dir=order_dir,
search_regex=search_regex, search_value=search_value,
custom_where=custom_where, search_regex=search_regex,
group_by='ip_address', custom_where=custom_where,
kwargs=kwargs) group_by='ip_address',
kwargs=kwargs)
except:
logger.warn("Unable to open PlexWatch database.")
return {'recordsFiltered': 0,
'recordsTotal': 0,
'data': 'null'},
results = query['result'] results = query['result']
@ -207,18 +221,23 @@ class PlexWatch(object):
julianday(datetime(time, "unixepoch", "localtime"))) * 86400) - \ julianday(datetime(time, "unixepoch", "localtime"))) * 86400) - \
(case when paused_counter is null then 0 else paused_counter end) as duration' (case when paused_counter is null then 0 else paused_counter end) as duration'
] ]
try:
query = data_tables.ssp_query(table_name=self.get_history_table_name(), query = data_tables.ssp_query(table_name=self.get_history_table_name(),
columns=columns, columns=columns,
start=start, start=start,
length=length, length=length,
order_column=int(order_column), order_column=int(order_column),
order_dir=order_dir, order_dir=order_dir,
search_value=search_value, search_value=search_value,
search_regex=search_regex, search_regex=search_regex,
custom_where=custom_where, custom_where=custom_where,
group_by='', group_by='',
kwargs=kwargs) kwargs=kwargs)
except:
logger.warn("Unable to open PlexWatch database.")
return {'recordsFiltered': 0,
'recordsTotal': 0,
'data': 'null'},
history = query['result'] history = query['result']
@ -380,14 +399,18 @@ class PlexWatch(object):
if not limit.isdigit(): if not limit.isdigit():
limit = '10' limit = '10'
if user: try:
query = 'SELECT time, user, xml FROM %s WHERE user = "%s" ORDER BY time DESC LIMIT %s' % \ if user:
(self.get_user_table_name(), user, limit) query = 'SELECT time, user, xml FROM %s WHERE user = "%s" ORDER BY time DESC LIMIT %s' % \
xml = myDB.select(query) (self.get_user_table_name(), user, limit)
else: xml = myDB.select(query)
query = 'SELECT time, user, xml FROM %s ORDER BY time DESC LIMIT %s' % \ else:
(self.get_user_table_name(), limit) query = 'SELECT time, user, xml FROM %s ORDER BY time DESC LIMIT %s' % \
xml = myDB.select(query) (self.get_user_table_name(), limit)
xml = myDB.select(query)
except:
logger.warn("Unable to open PlexWatch database.")
return None
for row in xml: for row in xml:
xml_data = helpers.latinToAscii(row[2]) xml_data = helpers.latinToAscii(row[2])
@ -435,10 +458,14 @@ class PlexWatch(object):
else: else:
where = 'WHERE user = "%s"' % user where = 'WHERE user = "%s"' % user
query = 'SELECT (SUM(stopped - time) - SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ try:
'COUNT(id) AS total_plays ' \ query = 'SELECT (SUM(stopped - time) - SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \
'FROM %s %s' % (self.get_user_table_name(), where) 'COUNT(id) AS total_plays ' \
result = myDB.select(query) 'FROM %s %s' % (self.get_user_table_name(), where)
result = myDB.select(query)
except:
logger.warn("Unable to open PlexWatch database.")
return None
for item in result: for item in result:
if item[0]: if item[0]:
@ -455,4 +482,84 @@ class PlexWatch(object):
user_watch_time_stats.append(row) user_watch_time_stats.append(row)
return user_watch_time_stats return user_watch_time_stats
def get_user_platform_stats(self, user=None):
myDB = db.DBConnection()
platform_stats = []
result_id = 0
try:
query = 'SELECT platform, COUNT(platform) as platform_count, xml ' \
'FROM %s ' \
'WHERE user = "%s" ' \
'GROUP BY platform ' \
'ORDER BY platform_count DESC' % (self.get_user_table_name(), user)
result = myDB.select(query)
except:
logger.warn("Unable to open PlexWatch database.")
return None
for item in result:
xml_data = helpers.latinToAscii(item[2])
try:
xml_parse = minidom.parseString(xml_data)
except:
logger.warn("Error parsing XML for Plex stream data.")
return None
xml_head = xml_parse.getElementsByTagName('Player')
if not xml_head:
logger.warn("Error parsing XML for Plex stream data.")
return None
for a in xml_head:
platform_type = self.get_xml_attr(a, 'platform')
row = {'platform_name': item[0],
'platform_type': platform_type,
'total_plays': item[1],
'result_id': result_id
}
platform_stats.append(row)
result_id += 1
return platform_stats
def get_user_gravatar_image(self, user=None):
myDB = db.DBConnection()
user_info = None
try:
query = 'SELECT xml ' \
'FROM %s ' \
'WHERE user = "%s" ' \
'ORDER BY id DESC LIMIT 1' % (self.get_user_table_name(), user)
result = myDB.select_single(query)
except:
logger.warn("Unable to open PlexWatch database.")
return None
xml_data = helpers.latinToAscii(result)
try:
xml_parse = minidom.parseString(xml_data)
except:
logger.warn("Error parsing XML for Plexwatch Database.")
return None
xml_head = xml_parse.getElementsByTagName('User')
if not xml_head:
logger.warn("Error parsing XML for Plexwatch Database.")
return None
for a in xml_head:
user_id = self.get_xml_attr(a, 'id')
user_thumb = self.get_xml_attr(a, 'thumb')
user_info = {'user_id': user_id,
'user_thumb': user_thumb}
return user_info

View file

@ -434,7 +434,6 @@ class WebInterface(object):
logger.warn(msg) logger.warn(msg)
return msg return msg
@cherrypy.expose @cherrypy.expose
def get_pms_token(self): def get_pms_token(self):
@ -447,7 +446,6 @@ class WebInterface(object):
logger.warn('Unable to retrieve Plex.tv token.') logger.warn('Unable to retrieve Plex.tv token.')
return False return False
@cherrypy.expose @cherrypy.expose
def get_pms_sessions_json(self, **kwargs): def get_pms_sessions_json(self, **kwargs):
@ -543,7 +541,7 @@ class WebInterface(object):
if result: if result:
return serve_template(templatename="user_recently_watched.html", recently_watched=result, title="Recently Watched") return serve_template(templatename="user_recently_watched.html", recently_watched=result, title="Recently Watched")
else: else:
return serve_template(templatename="user_recently_watched.html", recently_watched='', title="Recently Watched") return serve_template(templatename="user_recently_watched.html", recently_watched=None, title="Recently Watched")
logger.warn('Unable to retrieve data.') logger.warn('Unable to retrieve data.')
@cherrypy.expose @cherrypy.expose
@ -555,7 +553,19 @@ class WebInterface(object):
if result: if result:
return serve_template(templatename="user_watch_time_stats.html", watch_stats=result, title="Watch Stats") return serve_template(templatename="user_watch_time_stats.html", watch_stats=result, title="Watch Stats")
else: else:
return serve_template(templatename="user_watch_time_stats.html", watch_stats='', title="Watch Stats") return serve_template(templatename="user_watch_time_stats.html", watch_stats=None, title="Watch Stats")
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_user_platform_stats(self, user=None, **kwargs):
plex_watch = plexwatch.PlexWatch()
result = plex_watch.get_user_platform_stats(user)
if result:
return serve_template(templatename="user_platform_stats.html", platform_stats=result, title="Platform Stats")
else:
return serve_template(templatename="user_platform_stats.html", platform_stats=None, title="Platform Stats")
logger.warn('Unable to retrieve data.') logger.warn('Unable to retrieve data.')
@cherrypy.expose @cherrypy.expose
@ -637,6 +647,30 @@ class WebInterface(object):
plex_watch = plexwatch.PlexWatch() plex_watch = plexwatch.PlexWatch()
result = plex_watch.get_user_watch_time_stats(user) result = plex_watch.get_user_watch_time_stats(user)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
else:
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_platform_stats(self, user=None, **kwargs):
plex_watch = plexwatch.PlexWatch()
result = plex_watch.get_user_platform_stats(user)
if result:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
else:
logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_user_gravatar_image(self, user=None, **kwargs):
plex_watch = plexwatch.PlexWatch()
result = plex_watch.get_user_gravatar_image(user)
if result: if result:
cherrypy.response.headers['Content-type'] = 'application/json' cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result) return json.dumps(result)