New class to handle Datatables requests (very rough still)

Start cleaning up webserve
This commit is contained in:
Tim 2015-06-19 14:40:29 +02:00
parent 253a1efb3a
commit 7e238d6e62
6 changed files with 594 additions and 224 deletions

View file

@ -34,11 +34,12 @@
<th align='left' id="ip_address"><i class='fa fa-sort'></i> IP Address</th> <th align='left' id="ip_address"><i class='fa fa-sort'></i> IP Address</th>
<th align='left' id="title"><i class='fa fa-sort'></i> Title</th> <th align='left' id="title"><i class='fa fa-sort'></i> Title</th>
<th align='left' id="started"><i class='fa fa-sort'></i> Started</th> <th align='left' id="started"><i class='fa fa-sort'></i> Started</th>
<th align='left' id="paused"><i class='fa fa-sort'></i> Paused</th> <th align='left' id="paused_counter"><i class='fa fa-sort'></i> Paused</th>
<th align='left' id="stopped"><i class='fa fa-sort'></i> Stopped</th> <th align='left' id="stopped"><i class='fa fa-sort'></i> Stopped</th>
<th align='left' id="duration"><i class='fa fa-sort'></i> Duration</th> <th align='left' id="duration"><i class='fa fa-sort'></i> Duration</th>
<th align='left' id="percent_complete"> Completed</th> <th align='left' id="percent_complete"> Completed</th>
<th align='left' id="rating_key"> RatingKey</th> <th align='left' id="rating_key"> RatingKey</th>
<th align='left' id="xml"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -134,7 +135,8 @@
{ {
"targets": [0], "targets": [0],
"data":"id", "data":"id",
"visible": false "visible": false,
"searchable": false
}, },
{ {
"targets": [1], "targets": [1],
@ -146,7 +148,8 @@
} else { } else {
$(td).html(moment(cellData,"X").format("${date_format}")); $(td).html(moment(cellData,"X").format("${date_format}"));
} }
} },
"searchable": false
}, },
{ {
"targets": [2], "targets": [2],
@ -185,14 +188,16 @@
"data":"started", "data":"started",
"render": function ( data, type, full ) { "render": function ( data, type, full ) {
return moment(data, "X").format("${time_format}"); return moment(data, "X").format("${time_format}");
} },
"searchable": false
}, },
{ {
"targets": [7], "targets": [7],
"data":"paused", "data":"paused_counter",
"render": function ( data, type, full ) { "render": function ( data, type, full ) {
return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins'; return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins';
} },
"searchable": false
}, },
{ {
"targets": [8], "targets": [8],
@ -203,7 +208,8 @@
} else { } else {
return data; return data;
} }
} },
"searchable": false
}, },
{ {
"targets": [9], "targets": [9],
@ -214,7 +220,8 @@
} else { } else {
return data; return data;
} }
} },
"searchable": false
}, },
{ {
"targets": [10], "targets": [10],
@ -226,12 +233,20 @@
} else { } else {
return '<span class="badge">100%</span>'; return '<span class="badge">100%</span>';
} }
} },
"searchable": false
}, },
{ {
"targets": [11], "targets": [11],
"data":"rating_key", "data":"rating_key",
"visible": false "visible": false,
"searchable": false
},
{
"targets": [12],
"data":"xml",
"searchable":false,
"visible":false
} }
], ],
"drawCallback": function (settings) { "drawCallback": function (settings) {

View file

@ -0,0 +1,152 @@
<%inherit file="base.html"/>
<%!
from plexpy import helpers
%>
<%def name="headIncludes()">
<link rel="stylesheet" href="interfaces/default/css/plexwatch-tables.css">
</%def>
<%def name="body()">
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="user-info-wrapper">
<div class="user-info-poster-face">
<img src="interfaces/default/images/gravatar-default-80x80.png">
</div>
<div class="user-info-username">
Username
</div>
<div class="user-info-nav">
<ul class="user-info-nav">
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
<li><a id="ip-tab-btn" href="#userAddresses" data-toggle="tab">IP Addresses</a></li>
<li><a href="#userHistory" data-toggle="tab">History</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="tab-content">
<div class="tab-pane active" id="profile">
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Global Stats</h3>
</div>
</div>
<div id="user-time-stats" class="user-overview-stats-wrapper">
<div id="user-stats-spinner" class="spinner"></div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Platform Stats</h3>
</div>
</div>
<div id="user-platform-stats" class="user-platforms">
<div id="user-platform-spinner" class="spinner"></div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Recently watched</h3>
</div>
</div>
<div id="user-recently-watched" class="dashboard-recent-media-row">
<div id="user-watched-spinner" class="spinner"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="userAddresses">
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Public IP Addresses for <strong>
Username
</strong></h3>
</div>
</div>
<table id="tableUserIpAddresses" class="display" width="100%">
<thead>
<tr>
<th align="left"><i class="fa fa-sort"></i> Last seen</th>
<th align="left"><i class="fa fa-sort"></i> IP Address</th>
<th align="left"><i class="fa fa-sort"></i> Play Count</th>
<th align="left"><i class="fa fa-sort"></i> Platform (Last Seen)</th>
<th align="left"><i class="fa fa-sort"></i> Location</th>
<th align="left"><i class="fa fa-sort"></i> Location</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="userHistory">
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Watching History for <strong>
Username
</strong></h3>
</div>
</div>
Table
<div id="info-modal" class="modal hide fade" tabindex="-1"
role="dialog" aria-labelledby="info-modal" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true"><i class="fa fa-remove"></i></button>
<h3 id="myModalLabel">Stream Info: <span id="modal-stream-info"></span></h3>
</div>
<div class="modal-body" id="modal-text"></div>
<div class="modal-footer"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer></footer>
</%def>
<%def name="javascriptIncludes()">
<script src="interfaces/default/js/jquery.dataTables.min.js"></script>
<script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script>
<script src="interfaces/default/js/moment-with-locale.js"></script>
<script>
</script>
</%def>

View file

@ -57,7 +57,7 @@
"lengthMenu":"Show _MENU_ entries per page", "lengthMenu":"Show _MENU_ entries per page",
"info":"Showing _START_ to _END_ of _TOTAL_ active users", "info":"Showing _START_ to _END_ of _TOTAL_ active users",
"infoEmpty":"Showing 0 to 0 of 0 entries", "infoEmpty":"Showing 0 to 0 of 0 entries",
"infoFiltered":"(filtered from _MAX_ total entries)", "infoFiltered":"",
"emptyTable": "No data in table", "emptyTable": "No data in table",
}, },
"destroy": true, "destroy": true,
@ -80,11 +80,11 @@
"targets": [0], "targets": [0],
"data": null, "data": null,
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
if (rowData['user_thumb'] === '') { //if (rowData['user_thumb'] === '') {
$(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="' + rowData['user_thumb'] + '" alt="User Logo"/>');
} //}
}, },
"orderable": false, "orderable": false,
"className": "users-poster-face", "className": "users-poster-face",
@ -103,7 +103,8 @@
}, },
{ {
"targets": [3], "targets": [3],
"data": "ip_address" "data": "ip_address",
"searchable": false
}, },
{ {
"targets": [4], "targets": [4],

181
plexpy/datatables.py Normal file
View file

@ -0,0 +1,181 @@
# This file is part of PlexPy.
#
# PlexPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PlexPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
# TODO: Implement with sqlite3 directly instead of using db class
from plexpy import logger, helpers, db
import re
class DataTables(object):
"""
Server side processing for Datatables
"""
def __init__(self):
self.ssp_db = db.DBConnection()
def ssp_query(self, table_name,
columns=[],
start=0,
length=0,
order_column=0,
order_dir='asc',
search_value='',
search_regex='',
custom_where='',
group_by='',
kwargs=None):
parameters = self.process_kwargs(kwargs)
if group_by != '':
grouping = True
else:
grouping = False
column_data = self.extract_columns(columns)
where = self.construct_where(column_data, search_value, grouping, parameters)
order = self.construct_order(column_data, order_column, order_dir, parameters)
# TODO: custom_where is ugly and causes issues with reported total results
if custom_where != '':
where += 'AND (' + custom_where + ')'
if grouping:
query = 'SELECT * FROM (SELECT %s FROM %s GROUP BY %s) %s %s' \
% (column_data['column_string'], table_name, group_by,
where, order)
else:
query = 'SELECT %s FROM %s %s %s' \
% (column_data['column_string'], table_name, where,
order)
filtered = self.ssp_db.select(query)
if search_value == '':
totalcount = len(filtered)
else:
totalcount = self.ssp_db.select('SELECT COUNT(*) from %s' % table_name)[0][0]
# logger.debug(u"Query string: %s" % query)
result = filtered[start:(start + length)]
output = {'result': result,
'filteredCount': len(filtered),
'totalCount': totalcount}
return output
@staticmethod
def construct_order(column_data, order_column, order_dir, parameters=None):
order = ''
if parameters:
for parameter in parameters:
if parameter['data'] != '':
if int(order_column) == parameters.index(parameter):
if parameter['data'] in column_data['column_named'] and parameter['orderable'] == 'true':
order = 'ORDER BY %s COLLATE NOCASE %s' % (parameter['data'], order_dir)
logger.debug(u"order string %s " % order)
else:
order = 'ORDER BY %s COLLATE NOCASE %s' % (column_data['column_named'][order_column], order_dir)
logger.debug(u"order string (NO param received) %s " % order)
return order
@staticmethod
def construct_where(column_data, search_value='', grouping=False, parameters=None):
if search_value != '':
where = 'WHERE '
if parameters:
for column in column_data['column_named']:
search_skip = False
for parameter in parameters:
if column in parameter['data']:
if parameter['searchable'] == 'true':
logger.debug(u"Column %s is searchable." % column)
where += column + ' LIKE "%' + search_value + '%" OR '
search_skip = True
else:
logger.debug(u"Column %s is NOT searchable." % column)
search_skip = True
if not search_skip:
where += column + ' LIKE "%' + search_value + '%" OR '
else:
for column in column_data['column_named']:
where += column + ' LIKE "%' + search_value + '%" OR '
# TODO: This will break the query if all parameters are excluded
where = where[:-4]
return where
else:
return ''
@staticmethod
def extract_columns(columns=[]):
columns_string = ''
columns_literal = []
columns_named = []
for column in columns:
columns_string += column
columns_string += ', '
# TODO: make this case insensitive
if ' as ' in column:
columns_literal.append(column.rpartition(' as ')[0])
columns_named.append(column.rpartition(' as ')[-1])
else:
columns_literal.append(column)
columns_named.append(column)
columns_string = columns_string[:-2]
column_data = {'column_string': columns_string,
'column_literal': columns_literal,
'column_named': columns_named
}
return column_data
# TODO: Fix this method. Should not break if kwarg list is not sorted.
@staticmethod
def process_kwargs(kwargs):
column_parameters = []
for kwarg in sorted(kwargs):
if re.search(r"\[(\w+)\]", kwarg) and kwarg[:7] == 'columns':
parameters = re.findall(r"\[(\w+)\]", kwarg)
array_index = ''
for parameter in parameters:
pass_complete = False
if parameter.isdigit():
array_index = parameter
if parameter == 'data':
data = kwargs.get('columns[' + array_index + '][data]', "")
if parameter == 'orderable':
orderable = kwargs.get('columns[' + array_index + '][orderable]', "")
if parameter == 'searchable':
searchable = kwargs.get('columns[' + array_index + '][searchable]', "")
pass_complete = True
if pass_complete:
row = {'index': int(array_index),
'data': data,
'searchable': searchable,
'orderable': orderable}
column_parameters.append(row)
return sorted(column_parameters, key=lambda i: i['index'])

218
plexpy/plexwatch.py Normal file
View file

@ -0,0 +1,218 @@
# This file is part of PlexPy.
#
# PlexPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PlexPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, helpers, request, datatables, config
from xml.dom import minidom
import plexpy
import json
class PlexWatch(object):
"""
Retrieve and process data from the plexwatch database
"""
def __init__(self):
pass
@staticmethod
def get_history_table_name():
if plexpy.CONFIG.GROUPING_GLOBAL_HISTORY:
return "grouped"
else:
return "processed"
@staticmethod
def get_user_table_name():
if plexpy.CONFIG.GROUPING_USER_HISTORY:
return "grouped"
else:
return "processed"
def get_user_list(self, start='', length='', kwargs=None):
data_tables = datatables.DataTables()
start = int(start)
length = int(length)
filtered = []
totalcount = 0
search_value = ""
search_regex = ""
order_column = 1
order_dir = "desc"
if 'order[0][dir]' in kwargs:
order_dir = kwargs.get('order[0][dir]', "desc")
if 'order[0][column]' in kwargs:
order_column = kwargs.get('order[0][column]', 1)
if 'search[value]' in kwargs:
search_value = kwargs.get('search[value]', "")
if 'search[regex]' in kwargs:
search_regex = kwargs.get('search[regex]', "")
columns = ['user',
'time',
'ip_address',
'COUNT(title) as plays']
query = data_tables.ssp_query(table_name=self.get_user_table_name(),
columns=columns,
start=start,
length=length,
order_column=int(order_column),
order_dir=order_dir,
search_value=search_value,
search_regex=search_regex,
custom_where='',
group_by='user',
kwargs=kwargs)
users = query['result']
rows = []
for item in users:
row = {"plays": item['plays'],
"time": item['time'],
"user": item["user"],
"ip_address": item["ip_address"]
}
rows.append(row)
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
'data': rows,
}
return dict
def get_history(self, start='', length='', kwargs=None):
data_tables = datatables.DataTables()
start = int(start)
length = int(length)
filtered = []
totalcount = 0
search_value = ""
search_regex = ""
order_column = 1
order_dir = "desc"
if 'order[0][dir]' in kwargs:
order_dir = kwargs.get('order[0][dir]', "desc")
if 'order[0][column]' in kwargs:
order_column = kwargs.get('order[0][column]', "1")
if 'search[value]' in kwargs:
search_value = kwargs.get('search[value]', "")
if 'search[regex]' in kwargs:
search_regex = kwargs.get('search[regex]', "")
columns = ['id',
'time as date',
'user',
'platform',
'ip_address',
'title',
'time as started',
'paused_counter',
'stopped',
'ratingKey as rating_key',
'xml',
'round((julianday(datetime(stopped, "unixepoch", "localtime")) - \
julianday(datetime(time, "unixepoch", "localtime"))) * 86400) - \
(case when paused_counter is null then 0 else paused_counter end) as duration'
]
query = data_tables.ssp_query(table_name=self.get_history_table_name(),
columns=columns,
start=start,
length=length,
order_column=int(order_column),
order_dir=order_dir,
search_value=search_value,
search_regex=search_regex,
custom_where='',
group_by='',
kwargs=kwargs)
history = query['result']
rows = []
# NOTE: We are adding in a blank xml field in order enable the Datatables "searchable" parameter
for item in history:
row = {"id": item['id'],
"date": item['date'],
"user": item["user"],
"platform": item["platform"],
"ip_address": item["ip_address"],
"title": item["title"],
"started": item["started"],
"paused_counter": item["paused_counter"],
"stopped": item["stopped"],
"rating_key": item["rating_key"],
"duration": item["duration"],
"percent_complete": 0,
"xml": ""}
if item['paused_counter'] > 0:
row['paused_counter'] = item['paused_counter']
else:
row['paused_counter'] = 0
if item['started']:
if item['stopped'] > 0:
stopped = item['stopped']
else:
stopped = 0
if item['paused_counter'] > 0:
paused_counter = item['paused_counter']
else:
paused_counter = 0
try:
xml_parse = minidom.parseString(helpers.latinToAscii(item['xml']))
except IOError, e:
logger.warn("Error parsing XML in PlexWatch db: %s" % e)
xml_head = xml_parse.getElementsByTagName('opt')
if not xml_head:
logger.warn("Error parsing XML in PlexWatch db: %s" % e)
for s in xml_head:
if s.getAttribute('duration') and s.getAttribute('viewOffset'):
view_offset = helpers.cast_to_float(s.getAttribute('viewOffset'))
duration = helpers.cast_to_float(s.getAttribute('duration'))
if duration > 0:
row['percent_complete'] = (view_offset / duration) * 100
else:
row['percent_complete'] = 0
rows.append(row)
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
'data': rows,
}
return dict

View file

@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>. # along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, db, helpers, notifiers, plextv, pmsconnect from plexpy import logger, db, helpers, notifiers, plextv, pmsconnect, plexwatch
from plexpy.helpers import checked, radio, today, cleanName from plexpy.helpers import checked, radio, today, cleanName
from xml.dom import minidom from xml.dom import minidom
@ -84,100 +84,18 @@ class WebInterface(object):
def users(self): def users(self):
return serve_template(templatename="users.html", title="Users") return serve_template(templatename="users.html", title="Users")
@cherrypy.expose
def user(self):
return serve_template(templatename="user.html", title="User")
@cherrypy.expose @cherrypy.expose
def get_user_list(self, start=0, length=100, **kwargs): def get_user_list(self, start=0, length=100, **kwargs):
start = int(start)
length = int(length)
filtered = []
totalcount = 0
search_value = ""
search_regex = ""
order_column = 1
order_dir = "desc"
if 'order[0][dir]' in kwargs: plex_watch = plexwatch.PlexWatch()
order_dir = kwargs.get('order[0][dir]', "desc") users = plex_watch.get_user_list(start, length, kwargs)
if 'order[0][column]' in kwargs:
order_column = kwargs.get('order[0][column]', "1")
if 'search[value]' in kwargs:
search_value = kwargs.get('search[value]', "")
if 'search[regex]' in kwargs:
search_regex = kwargs.get('search[regex]', "")
sortcolumn = 'user'
if order_column == '2':
sortcolumn = 'time'
elif order_column == '3':
sortcolumn = 'ip_address'
elif order_column == '4':
sortcolumn = 'plays'
myDB = db.DBConnection()
db_table = db.DBConnection().get_history_table_name()
if search_value == "":
query = 'SELECT COUNT(title) as plays, user, time, \
SUM(time) as timeTotal, SUM(stopped) as stoppedTotal, \
SUM(paused_counter) as paused_counterTotal, platform, \
ip_address, xml \
from %s GROUP by user ORDER by %s COLLATE NOCASE %s' % (db_table, sortcolumn, order_dir)
filtered = myDB.select(query)
totalcount = len(filtered)
else:
query = 'SELECT COUNT(title) as plays, user, time, \
SUM(time) as timeTotal, SUM(stopped) as stoppedTotal, \
SUM(paused_counter) as paused_counterTotal, platform, \
ip_address, xml \
from ' + db_table + ' WHERE user LIKE "%' + search_value + '%" \
GROUP by user' + ' ORDER by %s COLLATE NOCASE %s' % (sortcolumn, order_dir)
filtered = myDB.select(query)
totalcount = myDB.select('SELECT COUNT(*) from %s' % db_table)[0][0]
users = filtered[start:(start + length)]
rows = []
for item in users:
row = {"plays": item['plays'],
"time": item['time'],
"user": item["user"],
"timeTotal": item["timeTotal"],
"ip_address": item["ip_address"],
"stoppedTotal": item["stoppedTotal"],
"paused_counterTotal": item["paused_counterTotal"],
"platform": item["platform"]
}
try:
xml_parse = minidom.parseString(helpers.latinToAscii(item['xml']))
except IOError, e:
logger.warn("Error parsing XML in PlexWatch db: %s" % e)
xml_head = xml_parse.getElementsByTagName('User')
if not xml_head:
logger.warn("Error parsing XML in PlexWatch db: %s" % e)
for s in xml_head:
if s.getAttribute('thumb'):
row['user_thumb'] = s.getAttribute('thumb')
else:
row['user_thumb'] = ""
if s.getAttribute('id'):
row['user_id'] = s.getAttribute('id')
else:
row['user_id'] = ""
rows.append(row)
dict = {'recordsFiltered': len(filtered),
'recordsTotal': totalcount,
'data': rows,
}
s = json.dumps(dict)
cherrypy.response.headers['Content-type'] = 'application/json' cherrypy.response.headers['Content-type'] = 'application/json'
return s return json.dumps(users)
@cherrypy.expose @cherrypy.expose
def checkGithub(self): def checkGithub(self):
@ -415,127 +333,12 @@ class WebInterface(object):
@cherrypy.expose @cherrypy.expose
def getHistory_json(self, start=0, length=100, **kwargs): def getHistory_json(self, start=0, length=100, **kwargs):
start = int(start)
length = int(length)
filtered = []
totalcount = 0
search_value = ""
search_regex = ""
order_column = 1
order_dir = "desc"
if 'order[0][dir]' in kwargs: plex_watch = plexwatch.PlexWatch()
order_dir = kwargs.get('order[0][dir]', "desc") history = plex_watch.get_history(start, length, kwargs)
if 'order[0][column]' in kwargs:
order_column = kwargs.get('order[0][column]', "1")
if 'search[value]' in kwargs:
search_value = kwargs.get('search[value]', "")
if 'search[regex]' in kwargs:
search_regex = kwargs.get('search[regex]', "")
myDB = db.DBConnection()
db_table = db.DBConnection().get_history_table_name()
sortcolumn = 'time'
sortbyhavepercent = False
if order_column == '2':
sortcolumn = 'user'
if order_column == '3':
sortcolumn = 'platform'
elif order_column == '4':
sortcolumn = 'ip_address'
elif order_column == '5':
sortcolumn = 'title'
elif order_column == '6':
sortcolumn = 'time'
elif order_column == '7':
sortcolumn = 'paused_counter'
elif order_column == '8':
sortcolumn = 'stopped'
elif order_column == '9':
sortcolumn = 'duration'
if search_value == "":
query = 'SELECT id, time, user, platform, ip_address, title, time, paused_counter, stopped, ratingKey, xml, \
round((julianday(datetime(stopped, "unixepoch", "localtime")) - \
julianday(datetime(time, "unixepoch", "localtime"))) * 86400) - \
(case when paused_counter is null then 0 else paused_counter end) as duration \
from %s order by %s COLLATE NOCASE %s' % (db_table, sortcolumn, order_dir)
filtered = myDB.select(query)
totalcount = len(filtered)
else:
query = 'SELECT id, time, user, platform, ip_address, title, time, paused_counter, stopped, ratingKey, xml, \
round((julianday(datetime(stopped, "unixepoch", "localtime")) - \
julianday(datetime(time, "unixepoch", "localtime"))) * 86400) - \
(case when paused_counter is null then 0 else paused_counter end) as duration \
from ' + db_table + ' WHERE user LIKE "%' + search_value + '%" OR title LIKE "%' + search_value \
+ '%"' + 'ORDER BY %s COLLATE NOCASE %s' % (sortcolumn, order_dir)
filtered = myDB.select(query)
totalcount = myDB.select('SELECT COUNT(*) from processed')[0][0]
history = filtered[start:(start + length)]
rows = []
for item in history:
row = {"id": item['id'],
"date": item['time'],
"user": item["user"],
"platform": item["platform"],
"ip_address": item["ip_address"],
"title": item["title"],
"started": item["time"],
"paused": item["paused_counter"],
"stopped": item["stopped"],
"rating_key": item["ratingKey"],
"duration": item["duration"],
"percent_complete": 0,
}
if item['paused_counter'] > 0:
row['paused'] = item['paused_counter']
else:
row['paused'] = 0
if item['time']:
if item['stopped'] > 0:
stopped = item['stopped']
else:
stopped = 0
if item['paused_counter'] > 0:
paused_counter = item['paused_counter']
else:
paused_counter = 0
try:
xml_parse = minidom.parseString(helpers.latinToAscii(item['xml']))
except IOError, e:
logger.warn("Error parsing XML in PlexWatch db: %s" % e)
xml_head = xml_parse.getElementsByTagName('opt')
if not xml_head:
logger.warn("Error parsing XML in PlexWatch db: %s" % e)
for s in xml_head:
if s.getAttribute('duration') and s.getAttribute('viewOffset'):
view_offset = helpers.cast_to_float(s.getAttribute('viewOffset'))
duration = helpers.cast_to_float(s.getAttribute('duration'))
if duration > 0:
row['percent_complete'] = (view_offset / duration) * 100
else:
row['percent_complete'] = 0
rows.append(row)
dict = {'recordsFiltered': len(filtered),
'recordsTotal': totalcount,
'data': rows,
}
s = json.dumps(dict)
cherrypy.response.headers['Content-type'] = 'application/json' cherrypy.response.headers['Content-type'] = 'application/json'
return s return json.dumps(history)
@cherrypy.expose @cherrypy.expose
def getStreamDetails(self, id=0, **kwargs): def getStreamDetails(self, id=0, **kwargs):