Skip to content
Snippets Groups Projects
Commit f841a3a6 authored by Julian Rother's avatar Julian Rother
Browse files

Optimized time-based stats

parent 9ecfbd5e
No related branches found
No related tags found
No related merge requests found
...@@ -128,6 +128,7 @@ CREATE TABLE IF NOT EXISTS `log` ( ...@@ -128,6 +128,7 @@ CREATE TABLE IF NOT EXISTS `log` (
`ip` varchar(64), `ip` varchar(64),
`id` varchar(64), `id` varchar(64),
`time` datetime NOT NULL, `time` datetime NOT NULL,
`date` datetime NOT NULL,
`source` varchar(8), `source` varchar(8),
`object` varchar(10), `object` varchar(10),
`obj_id` INTEGER, `obj_id` INTEGER,
......
...@@ -552,7 +552,7 @@ def auth(): # For use with nginx auth_request ...@@ -552,7 +552,7 @@ def auth(): # For use with nginx auth_request
password = auth.password password = auth.password
if checkperm(perms, username=username, password=password): if checkperm(perms, username=username, password=password):
try: try:
modify('INSERT INTO log (ip, id, `time`, object, obj_id, path) VALUES (?, ?, ?, "video", ?, ?)', ip, cookie, datetime.now(), perms[0]['vid'], url) modify('INSERT INTO log (ip, id, `time`, `date`, object, obj_id, path) VALUES (?, ?, ?, "video", ?, ?)', ip, cookie, datetime.now(), datetime.combine(date.today(), time()), perms[0]['vid'], url)
except: except:
pass pass
r = make_response('OK', 200) r = make_response('OK', 200)
......
...@@ -36,46 +36,35 @@ def stats_generic(req, param=None): ...@@ -36,46 +36,35 @@ def stats_generic(req, param=None):
res['y'].append(row['y']) res['y'].append(row['y'])
return Response(json.dumps(res, default=plotly_date_handler), mimetype='application/json') return Response(json.dumps(res, default=plotly_date_handler), mimetype='application/json')
@app.route('/stats/viewsperday') @app.route('/stats/viewsperday/<req>')
@mod_required @app.route('/stats/viewsperday/<req>/<param>')
def stats_viewsperday(): def stats_viewsperday(req, param=None):
start = None queries = {
end = None 'lecture': '(SELECT log.date AS date, formats.description AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN formats ON formats.id = videos.video_format WHERE videos.lecture_id = ? GROUP BY log.date, videos.video_format) UNION (SELECT log.date AS date, "total" AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id WHERE videos.lecture_id = ? GROUP BY log.date)',
courseid = request.args.get('course', None) 'course': '(SELECT log.date AS date, formats.description AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN lectures ON lectures.id = videos.lecture_id JOIN formats ON formats.id = videos.video_format WHERE lectures.course_id = ? GROUP BY log.date, videos.video_format) UNION (SELECT log.date AS date, "total" AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN lectures ON lectures.id = videos.lecture_id WHERE lectures.course_id = ? GROUP BY log.date)',
lectureid = request.args.get('lecture', None) 'global': '(SELECT log.date AS date, formats.description AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN formats ON formats.id = videos.video_format GROUP BY log.date, videos.video_format) UNION (SELECT log.date AS date, "total" AS trace, COUNT(DISTINCT log.id) AS y FROM log GROUP BY log.date)',
videoid = request.args.get('video', None) 'courses': 'SELECT log.date AS date, courses.handle AS trace, COUNT(DISTINCT log.id) AS y FROM log JOIN videos ON videos.id = log.obj_id JOIN lectures ON lectures.id = videos.lecture_id JOIN courses ON courses.id = lectures.course_id GROUP BY log.date, courses.id'
rows = query('''SELECT log.id AS id, log.time AS time, formats.description AS fmt }
FROM log if req not in queries:
JOIN videos ON (videos.id = log.obj_id) return 404, 'Not found!'
JOIN lectures ON (lectures.id = videos.lecture_id) rows = query(queries[req], *(queries[req].count('?')*[param]))
JOIN courses ON (courses.id = lectures.course_id) traces = set()
JOIN formats ON (formats.id = videos.video_format)
WHERE (? OR courses.id = ?) AND (? OR lectures.id = ?) AND (? OR videos.id = ?)''',
courseid == None, courseid, lectureid == None, lectureid, videoid == None, videoid)
data = {} data = {}
if not end: start = None
for row in rows:
if not start or row['date'] < start:
start = row['date']
traces.add(row['trace'])
if row['date'] not in data:
data[row['date']] = {}
data[row['date']][row['trace']] = row['y']
end = date.today() end = date.today()
first = None x = []
fmts = ['total'] y = [{'name': trace, 'vals': []} for trace in traces]
for e in rows:
key = e['time'].date()
if not first or key < first:
first = key
if key not in data:
data[key] = {'total': set()}
if e['fmt'] not in data[key]:
data[key][e['fmt']] = set()
data[key]['total'].add(e['id'])
data[key][e['fmt']].add(e['id'])
if e['fmt'] not in fmts:
fmts.append(e['fmt'])
if not start:
start = first
res = {'times': [], 'views': [{'name': name, 'vals': []} for name in fmts]}
while start and start <= end: while start and start <= end:
res['times'].append(start.isoformat()) x.append(start)
for d in res['views']: for trace in y:
d['vals'].append(len(data.get(start, {}).get(d['name'], set()))) trace['vals'].append(data.get(start, {}).get(trace['name'], 0))
start += timedelta(days=1) start += timedelta(days=1)
return Response(json.dumps(res, default=date_json_handler), mimetype='application/json') return Response(json.dumps({'times': x, 'views': y}, default=plotly_date_handler), mimetype='application/json')
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
</table> </table>
</div> </div>
<div id="statview" class="col-xs-6" style="height:600px"></div> <div id="statview" class="col-xs-6" style="height:600px"></div>
{{stats_viewsperday("statview", "Abrufe pro Tag", course=course.id)}} {{stats_viewsperday("statview", "course", "Abrufer pro Tag", param=course.id)}}
<div id="lecture_views" class="col-xs-6" style="height:600px"></div> <div id="lecture_views" class="col-xs-6" style="height:600px"></div>
{{stats_generic("lecture_views", "lecture_views", "Abrufe pro Aufnahme", param=course.id, type="bar")}} {{stats_generic("lecture_views", "lecture_views", "Abrufe pro Aufnahme", param=course.id, type="bar")}}
{% endif %} {% endif %}
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
</table> </table>
</div> </div>
<div id="statview" class="col-xs-12" style="height:600px"></div> <div id="statview" class="col-xs-12" style="height:600px"></div>
{{stats_viewsperday("statview", "Abrufe pro Tag", lecture=lecture.id)}} {{stats_viewsperday("statview", "lecture", "Abrufer pro Tag", param=lecture.id)}}
{% endif %} {% endif %}
</div> </div>
</div> </div>
......
...@@ -246,20 +246,33 @@ $('#embedcodebtn').popover( ...@@ -246,20 +246,33 @@ $('#embedcodebtn').popover(
{% macro vtttime(time) %}{{ '%02d:%02d:%02d.000'|format( time//3600, (time//60)%60, time%60) }}{% endmacro %} {% macro vtttime(time) %}{{ '%02d:%02d:%02d.000'|format( time//3600, (time//60)%60, time%60) }}{% endmacro %}
{% macro stats_viewsperday(id, title, type="scatter", course=None, lecture=None, video=None) %} {% macro stats_viewsperday(id, req, title, type="scatter", param=None, maxtraces=7) %}
<script> <script>
$.ajax({ $.ajax({
method: "GET", method: "GET",
url: "{{url_for('stats_viewsperday', course=course, lecture=lecture, video=video)}}", url: "{{url_for('stats_viewsperday', req=req, param=param)}}",
dataType: "json", dataType: "json",
error: moderator.api.handleapierror, error: moderator.api.handleapierror,
success: function (data) { success: function (data) {
var traces = []; var traces = [];
for (var i = 1; i < data.views.length; i++) { for (var i = 0; i < data.views.length; i++) {
if (data.views[i].name == 'total' && data.views.length > 2)
traces.push({"x": data.times, "y": data.views[i].vals, "type": "{{type}}", "name": "gesamt", line: {"color": "black", "width": 2}});
else
traces.push({"x": data.times, "y": data.views[i].vals, "type": "{{type}}", "name": data.views[i].name, line: {"width": 1}}); traces.push({"x": data.times, "y": data.views[i].vals, "type": "{{type}}", "name": data.views[i].name, line: {"width": 1}});
} }
if (data.views.length > 2) traces.sort(function (a, b) {
traces.push({"x": data.times, "y": data.views[0].vals, "type": "{{type}}", "name": "gesamt", line: {"color": "black", "width": 2}}); amax = 0;
bmax = 0;
for (var i = 0; i < a.y.length; i++)
amax = Math.max(amax, a.y[i]);
for (var i = 0; i < b.y.length; i++)
bmax = Math.max(bmax, b.y[i]);
return bmax-amax;
});
for (var i = 0; i < traces.length; i++)
if (i > {{maxtraces}})
traces[i].visible = "legendonly";
var layout = { var layout = {
"title": "{{title}}", "title": "{{title}}",
"showlegend": (traces.length != 1) "showlegend": (traces.length != 1)
......
...@@ -27,7 +27,9 @@ ...@@ -27,7 +27,9 @@
<div id="categories_lectures" class="col-xs-6" style="height:600px"></div> <div id="categories_lectures" class="col-xs-6" style="height:600px"></div>
{{stats_pie("categories_lectures", "categories_lectures", "Aufnahmen nach Kategorie", showlegend=False)}} {{stats_pie("categories_lectures", "categories_lectures", "Aufnahmen nach Kategorie", showlegend=False)}}
<div id="statview" class="col-xs-12" style="height:600px"></div> <div id="statview" class="col-xs-12" style="height:600px"></div>
{{stats_viewsperday("statview", "Abrufe pro Tag")}} {{stats_viewsperday("statview", "global", "Abrufer pro Tag")}}
<div id="courseviewsperday" class="col-xs-12" style="height:600px"></div>
{{stats_viewsperday("courseviewsperday", "courses", "Abrufer pro Tag")}}
</div> </div>
i </div> i </div>
</div> </div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment